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

168 lines
3.8 KiB
C
Raw Normal View History

2016-05-27 00:40:30 +00:00
/*
2018-03-11 23:15:13 +00:00
* BIRD -- Internet Group Management Protocol (IGMP)
2016-05-27 00:40:30 +00:00
*
2018-03-11 23:15:13 +00:00
* (c) 2016 Ondrej Hlavaty <aearsis@eideo.cz>
* (c) 2018 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2018 CZ.NIC z.s.p.o.
2016-05-27 00:40:30 +00:00
*
2018-03-11 23:15:13 +00:00
* Can be freely distributed and used under the terms of the GNU GPL.
2016-05-27 00:40:30 +00:00
*/
#include "igmp.h"
#include "lib/checksum.h"
2018-03-11 23:15:13 +00:00
#include <linux/mroute.h>
struct igmp_packet
{
2016-05-27 00:40:30 +00:00
u8 type;
u8 resp_time;
u16 checksum;
u32 addr;
2018-03-11 23:15:13 +00:00
} PACKED;
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
#define IGMP_MSG_QUERY 0x11
#define IGMP_MSG_V1_REPORT 0x12
#define IGMP_MSG_V2_REPORT 0x16
#define IGMP_MSG_LEAVE 0x17
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
#define LOG_PKT(msg, args...) \
log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args)
2016-05-27 00:40:30 +00:00
int
2018-03-11 23:15:13 +00:00
igmp_rx_hook(sock *sk, uint len)
2016-05-27 00:40:30 +00:00
{
struct igmp_iface *ifa = sk->data;
struct igmp_proto *p = ifa->proto;
2018-03-11 23:15:13 +00:00
const char *err_dsc = NULL;
uint err_val = 0;
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
struct igmp_packet *pkt = (void *) sk_rx_buffer(sk, &len);
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
if (pkt == NULL)
DROP("bad IP header", len);
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
if (len < sizeof(struct igmp_packet))
DROP("too short", len);
2016-05-27 00:40:30 +00:00
if (!ipsum_verify(pkt, len, NULL))
2018-03-11 23:15:13 +00:00
DROP("invalid checksum", pkt->checksum);
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
ip4_addr from = ipa_to_ip4(sk->faddr);
ip4_addr addr = get_ip4(&pkt->addr);
switch (pkt->type)
{
case IGMP_MSG_QUERY:
TRACE(D_PACKETS, "Query received from %I4 on %s", from, ifa->iface->name);
/* FIXME: Warning if resp_time == 0 */
igmp_handle_query(ifa, addr, from, pkt->resp_time * (btime) 100000);
break;
case IGMP_MSG_V1_REPORT:
TRACE(D_PACKETS, "Report (v1) received from %I4 on %s for %I4", from, ifa->iface->name, addr);
igmp_handle_report(ifa, addr, 1);
break;
case IGMP_MSG_V2_REPORT:
TRACE(D_PACKETS, "Report (v2) received from %I4 on %s for %I4", from, ifa->iface->name, addr);
igmp_handle_report(ifa, addr, 2);
break;
case IGMP_MSG_LEAVE:
TRACE(D_PACKETS, "Leave received from %I4 on %s for %I4", from, ifa->iface->name, addr);
igmp_handle_leave(ifa, addr);
break;
default:
TRACE(D_PACKETS, "Unknown IGMP packet (0x%x) from %I4 on %s", pkt->type, from, ifa->iface->name);
break;
}
return 1;
2016-05-27 00:40:30 +00:00
drop:
2018-03-11 23:15:13 +00:00
LOG_PKT("Bad packet from %I on %s - %s (%u)",
sk->faddr, sk->iface->name, err_dsc, err_val);
return 1;
2016-05-27 00:40:30 +00:00
}
void
igmp_err_hook(sock *sk, int err)
{
struct igmp_iface *ifa = sk->data;
struct igmp_proto *p = ifa->proto;
2018-03-11 23:15:13 +00:00
log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->iface->name, err);
2016-05-27 00:40:30 +00:00
}
2018-03-11 23:15:13 +00:00
void
igmp_send_query(struct igmp_iface *ifa, ip4_addr addr, btime resp_time)
2016-05-27 00:40:30 +00:00
{
struct igmp_proto *p = ifa->proto;
2018-03-11 23:15:13 +00:00
struct igmp_packet *pkt = (void *) ifa->sk->tbuf;
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
pkt->type = IGMP_MSG_QUERY;
pkt->resp_time = resp_time / 100000;
2016-05-27 00:40:30 +00:00
put_ip4(&pkt->addr, addr);
pkt->checksum = 0;
2018-03-11 23:15:13 +00:00
pkt->checksum = ipsum_calculate(pkt, sizeof(struct igmp_packet), NULL);
2016-05-27 00:40:30 +00:00
ifa->sk->daddr = ip4_zero(addr) ? IP4_ALL_NODES : ipa_from_ip4(addr);
if (ip4_zero(addr))
2018-03-11 23:15:13 +00:00
TRACE(D_PACKETS, "Sending query on %s", ifa->iface->name);
2016-05-27 00:40:30 +00:00
else
2018-03-11 23:15:13 +00:00
TRACE(D_PACKETS, "Sending query on %s for %I4", ifa->iface->name, addr);
2016-05-27 00:40:30 +00:00
2018-03-11 23:15:13 +00:00
sk_send(ifa->sk, sizeof(struct igmp_packet));
2016-05-27 00:40:30 +00:00
}
int
2018-03-11 23:15:13 +00:00
igmp_open_socket(struct igmp_iface *ifa)
2016-05-27 00:40:30 +00:00
{
2018-03-11 23:15:13 +00:00
struct igmp_proto *p = ifa->proto;
sock *sk = sk_new(p->p.pool);
sk->type = SK_IP;
sk->dport = IGMP_PROTO;
sk->saddr = ifa->iface->addr4->ip;
2016-05-27 00:40:30 +00:00
sk->iface = ifa->iface;
sk->rx_hook = igmp_rx_hook;
sk->err_hook = igmp_err_hook;
2018-03-11 23:15:13 +00:00
sk->data = ifa;
2016-05-27 00:40:30 +00:00
sk->tbsize = ifa->iface->mtu;
2018-03-11 23:15:13 +00:00
sk->tos = IP_PREC_INTERNET_CONTROL;
sk->ttl = 1;
2016-05-27 00:40:30 +00:00
if (sk_open(sk) < 0)
goto err;
if (sk_setup_multicast(sk) < 0)
goto err;
2018-03-11 23:15:13 +00:00
if (sk_setup_igmp(sk, p->mif_group, ifa->mif) < 0)
goto err;
2016-05-27 00:40:30 +00:00
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;
2018-03-11 23:15:13 +00:00
return 1;
2016-05-27 00:40:30 +00:00
err:
log(L_ERR "%s: Socket error: %s%#m", ifa->proto->p.name, sk->err);
rfree(sk);
2018-03-11 23:15:13 +00:00
return 0;
2016-05-27 00:40:30 +00:00
}