Some vendors do not fill the checksum for IPv6 UDP packets.
For interoperability with such implementations one can set
UDP_NO_CHECK6_RX socket option on Linux.
Thanks to Ville O for the suggestion.
Minor changes by committer.
<code>
protocol bfd [<name>] {
accept [ipv4|ipv6] [direct|multihop];
+ zero udp6 checksum rx <switch>;
interface <interface pattern> {
interval <time>;
min rx interval <time>;
in cases like running multiple BIRD instances on a machine, each
handling a different set of interfaces. Default: disabled.
+ <tag><label id="bfd-zero-udp6-checksum-rx">zero udp6 checksum rx <m/switch/</tag>
+ UDP checksum computation is optional in IPv4 while it is mandatory in
+ IPv6. Some BFD implementations send UDP datagrams with zero (blank)
+ checksum even in IPv6 case. This option configures BFD listening sockets
+ to accept such datagrams. It is available only on platforms that support
+ the relevant socket option (e.g. <cf/UDP_NO_CHECK6_RX/ on Linux).
+ Default: disabled.
+
<tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
Interface definitions allow to specify options for sessions associated
with such interfaces and also may contain interface specific options.
#define SKF_HDRINCL 0x400 /* Used internally */
#define SKF_PKTINFO 0x800 /* Used internally */
+#define SKF_UDP6_NO_CSUM_RX 0x1000 /* Accept zero checksums for received UDPv6 packets */
+
/*
* Socket types SA SP DA DP IF TTL SendTo (?=may, -=must not, *=must)
*/
(new->accept_ipv6 != old->accept_ipv6) ||
(new->accept_direct != old->accept_direct) ||
(new->accept_multihop != old->accept_multihop) ||
- (new->strict_bind != old->strict_bind))
+ (new->strict_bind != old->strict_bind) ||
+ (new->zero_udp6_checksum_rx != old->zero_udp6_checksum_rx))
return 0;
birdloop_mask_wakeups(p->loop);
u8 accept_direct;
u8 accept_multihop;
u8 strict_bind;
+ u8 zero_udp6_checksum_rx;
};
struct bfd_iface_config
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
- STRICT, BIND)
+ STRICT, BIND, ZERO, UDP6, CHECKSUM, RX)
%type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local
| MULTIHOP bfd_multihop
| NEIGHBOR bfd_neighbor
| STRICT BIND bool { BFD_CFG->strict_bind = $3; }
+ | ZERO UDP6 CHECKSUM RX bool { BFD_CFG->zero_udp6_checksum_rx = $5; }
;
bfd_proto_opts:
sock *
bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
{
+ struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
+
sock *sk = sk_new(p->tpool);
sk->type = SK_UDP;
sk->subtype = af;
sk->priority = sk_priority_control;
sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
+ if (cf->zero_udp6_checksum_rx)
+ sk->flags |= SKF_UDP6_NO_CSUM_RX;
+
if (sk_open(sk) < 0)
goto err;
sock *
bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{
+ struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
+
sock *sk = sk_new(p->tpool);
sk->type = SK_UDP;
sk->saddr = local;
sk->priority = sk_priority_control;
sk->flags = SKF_THREAD | SKF_BIND | (ifa ? SKF_TTL_RX : 0);
+ if (cf->zero_udp6_checksum_rx)
+ sk->flags |= SKF_UDP6_NO_CSUM_RX;
+
if (sk_open(sk) < 0)
goto err;
{
ERR_MSG("Freebind is not supported");
}
+
+static inline int
+sk_set_udp6_no_csum_rx(sock *s)
+{
+ ERR_MSG("UDPv6 zero checksum is not supported");
+}
return 0;
}
+
+static inline int
+sk_set_udp6_no_csum_rx(sock *s)
+{
+ int y = 1;
+
+ if (setsockopt(s->fd, SOL_UDP, UDP_NO_CHECK6_RX, &y, sizeof(y)) < 0)
+ ERR("UDP_NO_CHECK6_RX");
+}
if (s->tos >= 0)
if (sk_set_tos6(s, s->tos) < 0)
return -1;
+
+ if ((s->flags & SKF_UDP6_NO_CSUM_RX) && (s->type == SK_UDP))
+ if (sk_set_udp6_no_csum_rx(s) < 0)
+ return -1;
}
/* Must be after sk_set_tos4() as setting ToS on Linux also mangles priority */