be71361df48c425003d6f7b5272255ab23960946
5 #include <netinet/ip6.h>
6 #include <netinet/icmp6.h>
8 #include <sys/socket.h>
9 #include <net/ethernet.h>
10 #include <netpacket/packet.h>
11 #include <linux/rtnetlink.h>
12 #include <linux/filter.h>
19 static int sock
= -1, rtnl
= -1;
20 static int if_index
= -1;
21 static int bfd_failed
= 0, bfd_limit
= 0, bfd_interval
= 0;
22 static bool bfd_armed
= false;
25 static void bfd_send(int signal
)
29 struct icmp6_hdr icmp6
;
31 memset(&ping
, 0, sizeof(ping
));
33 ping
.ip6
.ip6_vfc
= 6 << 4;
34 ping
.ip6
.ip6_plen
= htons(8);
35 ping
.ip6
.ip6_nxt
= IPPROTO_ICMPV6
;
36 ping
.ip6
.ip6_hlim
= 255;
38 ping
.icmp6
.icmp6_type
= ICMP6_ECHO_REQUEST
;
39 ping
.icmp6
.icmp6_data32
[0] = htonl(0xbfd0bfd);
42 struct odhcp6c_entry
*pd
= odhcp6c_get_state(STATE_IA_PD
, &pdlen
), *cpd
= NULL
;
43 struct odhcp6c_entry
*rt
= odhcp6c_get_state(STATE_RA_ROUTE
, &rtlen
), *crt
= NULL
;
44 bool crt_found
= false;
47 for (size_t i
= 0; i
< pdlen
/ sizeof(*pd
); ++i
)
48 if (!cpd
|| ((cpd
->target
.s6_addr
[0] & 7) == 0xfc) > ((pd
[i
].target
.s6_addr
[0] & 7) == 0xfc)
49 || cpd
->preferred
< pd
[i
].preferred
)
52 // Detect default router
53 for (size_t i
= 0; i
< rtlen
/ sizeof(*rt
); ++i
)
54 if (IN6_IS_ADDR_UNSPECIFIED(&rt
[i
].target
) && (!crt
|| crt
->priority
> rt
[i
].priority
))
57 struct sockaddr_ll dest
= {
58 .sll_family
= AF_PACKET
,
59 .sll_protocol
= htons(ETH_P_IPV6
),
60 .sll_ifindex
= if_index
,
61 .sll_halen
= ETH_ALEN
,
69 .hdr
= {sizeof(req
), RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
, 1, 0},
70 .ndm
= {.ndm_family
= AF_INET6
, .ndm_ifindex
= if_index
}
72 send(rtnl
, &req
, sizeof(req
), 0);
77 ssize_t read
= recv(rtnl
, buf
, sizeof(buf
), 0);
78 nhm
= (struct nlmsghdr
*)buf
;
79 if (read
< 0 || !NLMSG_OK(nhm
, (size_t)read
))
82 for (; read
> 0 && NLMSG_OK(nhm
, (size_t)read
); nhm
= NLMSG_NEXT(nhm
, read
)) {
83 ssize_t attrlen
= NLMSG_PAYLOAD(nhm
, sizeof(struct ndmsg
));
84 if (nhm
->nlmsg_type
!= RTM_NEWNEIGH
|| attrlen
<= 0) {
89 // Already have our MAC
93 struct ndmsg
*ndm
= NLMSG_DATA(nhm
);
94 for (struct rtattr
*rta
= (struct rtattr
*)&ndm
[1];
95 attrlen
> 0 && RTA_OK(rta
, (size_t)attrlen
);
96 rta
= RTA_NEXT(rta
, attrlen
)) {
97 if (rta
->rta_type
== NDA_DST
) {
98 crt_found
= IN6_ARE_ADDR_EQUAL(RTA_DATA(rta
), &crt
->router
);
99 } else if (rta
->rta_type
== NDA_LLADDR
) {
100 memcpy(dest
.sll_addr
, RTA_DATA(rta
), ETH_ALEN
);
107 if (!crt_found
|| !cpd
)
110 ping
.ip6
.ip6_src
= cpd
->target
;
111 ping
.ip6
.ip6_dst
= cpd
->target
;
114 if (bfd_failed
++ > bfd_limit
) {
121 uint16_t sum = cksum(&ping.ip6.ip6_src, sizeof(ping.ip6.ip6_src), 0);
122 sum = cksum(&ping.ip6.ip6_dst, sizeof(ping.ip6.ip6_dst), ~sum);
123 sum = cksum(&ping.ip6.ip6_plen, sizeof(ping.ip6.ip6_plen), ~sum);
125 uint8_t next[4] = {0, 0, 0, ping.ip6.ip6_nxt};
126 sum = cksum(next, sizeof(next), ~sum);
128 ping.icmp6.icmp6_cksum = cksum(&ping.icmp6, sizeof(ping.icmp6), ~sum);
131 struct sock_filter bpf
[] = {
132 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, offsetof(struct ip6_hdr
, ip6_plen
)),
133 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, htonl(8 << 16 | IPPROTO_ICMPV6
<< 8 | 254), 0, 13),
134 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, offsetof(struct ip6_hdr
, ip6_dst
)),
135 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, ping
.ip6
.ip6_dst
.s6_addr32
[0], 0, 11),
136 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, offsetof(struct ip6_hdr
, ip6_dst
) + 4),
137 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, ping
.ip6
.ip6_dst
.s6_addr32
[1], 0, 9),
138 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, offsetof(struct ip6_hdr
, ip6_dst
) + 8),
139 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, ping
.ip6
.ip6_dst
.s6_addr32
[2], 0, 7),
140 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, offsetof(struct ip6_hdr
, ip6_dst
) + 12),
141 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, ping
.ip6
.ip6_dst
.s6_addr32
[3], 0, 5),
142 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, sizeof(struct ip6_hdr
) +
143 offsetof(struct icmp6_hdr
, icmp6_type
)),
144 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, htonl(ICMP6_ECHO_REQUEST
<< 24), 0, 3),
145 BPF_STMT(BPF_LD
| BPF_W
| BPF_ABS
, sizeof(struct ip6_hdr
) +
146 offsetof(struct icmp6_hdr
, icmp6_data32
)),
147 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, ping
.icmp6
.icmp6_data32
[0], 0, 1),
148 BPF_STMT(BPF_RET
| BPF_K
, 0xffffffff),
149 BPF_STMT(BPF_RET
| BPF_K
, 0),
151 struct sock_fprog bpf_prog
= {sizeof(bpf
) / sizeof(*bpf
), bpf
};
153 setsockopt(sock
, SOL_SOCKET
, SO_DETACH_FILTER
, &bpf_prog
, sizeof(bpf_prog
));
154 if (setsockopt(sock
, SOL_SOCKET
, SO_ATTACH_FILTER
, &bpf_prog
, sizeof(bpf_prog
))) {
161 bind(sock
, (struct sockaddr
*)&dest
, sizeof(dest
));
163 while (recv(sock
, dummy
, sizeof(dummy
), MSG_DONTWAIT
| MSG_TRUNC
) > 0);
165 sendto(sock
, &ping
, sizeof(ping
), MSG_DONTWAIT
,
166 (struct sockaddr
*)&dest
, sizeof(dest
));
171 void bfd_receive(void)
174 while (recv(sock
, dummy
, sizeof(dummy
), MSG_DONTWAIT
| MSG_TRUNC
) > 0) {
181 int bfd_start(int ifindex
, int limit
, int interval
)
187 bfd_interval
= interval
;
189 if (limit
< 1 || interval
< 1)
192 rtnl
= socket(AF_NETLINK
, SOCK_DGRAM
| SOCK_CLOEXEC
, NETLINK_ROUTE
);
193 struct sockaddr_nl rtnl_kernel
= { .nl_family
= AF_NETLINK
};
194 connect(rtnl
, (const struct sockaddr
*)&rtnl_kernel
, sizeof(rtnl_kernel
));
196 sock
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_CLOEXEC
, htons(ETH_P_IPV6
));
199 fcntl(sock
, F_SETOWN
, getpid());
200 fcntl(sock
, F_SETFL
, fcntl(sock
, F_GETFL
) | O_ASYNC
);
202 signal(SIGALRM
, bfd_send
);
216 uint16_t cksum(const uint16_t *addr, size_t count, uint16_t start)
218 uint32_t sum = start;
226 sum = (sum & 0xffff) + (sum >> 16);