0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-11 03:21:53 +00:00
bird/proto/igmp/packets.c

153 lines
3.1 KiB
C
Raw Normal View History

2016-05-27 00:40:30 +00:00
/*
* BIRD --IGMP protocol
*
* (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "igmp.h"
#include "lib/checksum.h"
struct igmp_pkt {
u8 type;
u8 resp_time;
u16 checksum;
u32 addr;
};
#define IGMP_TP_MS_QUERY 0x11
#define IGMP_TP_V1_MS_REPORT 0x12
#define IGMP_TP_V2_MS_REPORT 0x16
#define IGMP_TP_LEAVE 0x17
#define DROP(args...) do { TRACE(D_PACKETS, "Dropping packet: " args); goto drop; } while(0)
int
igmp_accept(struct igmp_iface *ifa, ip4_addr from, struct igmp_pkt *pkt)
{
struct igmp_proto *p = ifa->proto;
if (pkt->type == IGMP_TP_MS_QUERY)
return igmp_query_received(ifa, from);
ip4_addr addr = get_ip4(&pkt->addr);
struct igmp_grp *grp = igmp_grp_find(ifa, &addr);
if (pkt->type == IGMP_TP_LEAVE)
return igmp_leave(grp, pkt->resp_time);
if (!grp)
grp = igmp_grp_new(ifa, &addr);
switch (pkt->type) {
case IGMP_TP_V1_MS_REPORT:
igmp_membership_report(grp, 1, 10);
break;
case IGMP_TP_V2_MS_REPORT:
igmp_membership_report(grp, 2, pkt->resp_time);
break;
default:
DROP("Unknown type");
break;
}
drop:
return 0;
}
int
igmp_rx_hook(sock *sk, int len)
{
struct igmp_iface *ifa = sk->data;
struct igmp_proto *p = ifa->proto;
struct igmp_pkt *pkt = (struct igmp_pkt *) sk_rx_buffer(sk, &len);
if (len < sizeof(struct igmp_pkt))
DROP("Shorter than 8 bytes");
/* Longer packets are in IGMPv3 */
if (len != 8)
DROP("Expected pkt length 8, not %i (probably newer IGMP)", len);
if (!ipsum_verify(pkt, len, NULL))
DROP("Invalid checksum");
return igmp_accept(sk->data, ipa_to_ip4(sk->faddr), pkt);
drop:
return 0;
}
void
igmp_err_hook(sock *sk, int err)
{
struct igmp_iface *ifa = sk->data;
struct igmp_proto *p = ifa->proto;
TRACE(D_EVENTS, "IGMP err %m", err);
}
int
igmp_tx_query(struct igmp_iface *ifa, ip4_addr addr)
{
struct igmp_proto *p = ifa->proto;
struct igmp_pkt *pkt = (struct igmp_pkt *) ifa->sk->tbuf;
pkt->type = IGMP_TP_MS_QUERY;
pkt->resp_time = (ifa->cf->query_response_int TO_MS) / 100;
put_ip4(&pkt->addr, addr);
pkt->checksum = 0;
pkt->checksum = ipsum_calculate(pkt, sizeof(struct igmp_pkt), NULL);
ifa->sk->daddr = ip4_zero(addr) ? IP4_ALL_NODES : ipa_from_ip4(addr);
if (ip4_zero(addr))
TRACE(D_PACKETS, "Sending general query on iface %s", ifa->iface->name);
else
TRACE(D_PACKETS, "Sending query to grp %I4 on iface %s", addr, ifa->iface->name);
sk_send(ifa->sk, 8);
return 0;
}
int
igmp_sk_open(struct igmp_iface *ifa)
{
sock *sk = sk_new(ifa->proto->p.pool);
sk->type = SK_IGMP;
sk->saddr = ifa->iface->addr->ip;
sk->iface = ifa->iface;
sk->data = ifa;
sk->ttl = 1;
sk->tos = IP_PREC_INTERNET_CONTROL;
sk->rx_hook = igmp_rx_hook;
sk->err_hook = igmp_err_hook;
sk->tbsize = ifa->iface->mtu;
if (sk_open(sk) < 0)
goto err;
if (sk_setup_multicast(sk) < 0)
goto err;
if (sk_join_group(sk, IP4_IGMP_ROUTERS) < 0)
goto err;
if (sk_join_group(sk, IP4_ALL_ROUTERS) < 0)
goto err;
ifa->sk = sk;
return 0;
err:
log(L_ERR "%s: Socket error: %s%#m", ifa->proto->p.name, sk->err);
rfree(sk);
return -1;
}