2 * Copyright (C) 2012-2013 Steven Barth <steven@midlink.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License v2 as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
22 #include <arpa/inet.h>
23 #include <sys/socket.h>
24 #include <net/ethernet.h>
25 #include <netinet/ip6.h>
26 #include <netinet/icmp6.h>
27 #include <netpacket/packet.h>
29 #include <linux/rtnetlink.h>
30 #include <linux/filter.h>
32 #include <netlink/msg.h>
33 #include <netlink/socket.h>
34 #include <netlink/attr.h>
41 struct odhcpd_event ev
;
46 static void handle_solicit(void *addr
, void *data
, size_t len
,
47 struct interface
*iface
, void *dest
);
48 static void handle_rtnl_event(struct odhcpd_event
*ev
);
49 static int cb_rtnl_valid(struct nl_msg
*msg
, void *arg
);
50 static void catch_rtnl_err(struct odhcpd_event
*e
, int error
);
52 static int addr6_dump_rqs
= 0;
53 static int ping_socket
= -1;
54 static struct event_socket rtnl_event
= {
56 .uloop
= {.fd
= - 1, },
58 .handle_error
= catch_rtnl_err
,
59 .recv_msgs
= handle_rtnl_event
,
62 .sock_bufsize
= 133120,
65 // Filter ICMPv6 messages of type neighbor soliciation
66 static struct sock_filter bpf
[] = {
67 BPF_STMT(BPF_LD
| BPF_B
| BPF_ABS
, offsetof(struct ip6_hdr
, ip6_nxt
)),
68 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, IPPROTO_ICMPV6
, 0, 3),
69 BPF_STMT(BPF_LD
| BPF_B
| BPF_ABS
, sizeof(struct ip6_hdr
) +
70 offsetof(struct icmp6_hdr
, icmp6_type
)),
71 BPF_JUMP(BPF_JMP
| BPF_JEQ
| BPF_K
, ND_NEIGHBOR_SOLICIT
, 0, 1),
72 BPF_STMT(BPF_RET
| BPF_K
, 0xffffffff),
73 BPF_STMT(BPF_RET
| BPF_K
, 0),
75 static const struct sock_fprog bpf_prog
= {sizeof(bpf
) / sizeof(*bpf
), bpf
};
78 // Initialize NDP-proxy
83 rtnl_event
.sock
= odhcpd_create_nl_socket(NETLINK_ROUTE
);
87 rtnl_event
.ev
.uloop
.fd
= nl_socket_get_fd(rtnl_event
.sock
);
89 if (nl_socket_set_buffer_size(rtnl_event
.sock
, rtnl_event
.sock_bufsize
, 0))
92 nl_socket_disable_seq_check(rtnl_event
.sock
);
94 nl_socket_modify_cb(rtnl_event
.sock
, NL_CB_VALID
, NL_CB_CUSTOM
,
97 // Receive IPv6 address, IPv6 routes and neighbor events
98 if (nl_socket_add_memberships(rtnl_event
.sock
, RTNLGRP_IPV6_IFADDR
,
99 RTNLGRP_IPV6_ROUTE
, RTNLGRP_NEIGH
, 0))
102 odhcpd_register(&rtnl_event
.ev
);
104 // Open ICMPv6 socket
105 ping_socket
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
, IPPROTO_ICMPV6
);
106 if (ping_socket
< 0) {
107 syslog(LOG_ERR
, "Unable to open raw socket: %s", strerror(errno
));
111 setsockopt(ping_socket
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
));
113 // This is required by RFC 4861
115 setsockopt(ping_socket
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
));
116 setsockopt(ping_socket
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
, sizeof(val
));
118 // Filter all packages, we only want to send
119 struct icmp6_filter filt
;
120 ICMP6_FILTER_SETBLOCKALL(&filt
);
121 setsockopt(ping_socket
, IPPROTO_ICMPV6
, ICMP6_FILTER
, &filt
, sizeof(filt
));
126 if (rtnl_event
.sock
) {
127 nl_socket_free(rtnl_event
.sock
);
128 rtnl_event
.sock
= NULL
;
129 rtnl_event
.ev
.uloop
.fd
= -1;
135 static void dump_neigh_table(const bool proxy
)
139 .ndm_family
= AF_INET6
,
140 .ndm_flags
= proxy
? NTF_PROXY
: 0,
143 msg
= nlmsg_alloc_simple(RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
);
147 nlmsg_append(msg
, &ndm
, sizeof(ndm
), 0);
149 nl_send_auto_complete(rtnl_event
.sock
, msg
);
154 static void dump_addr6_table(void)
157 struct ifaddrmsg ifa
= {
158 .ifa_family
= AF_INET6
,
161 msg
= nlmsg_alloc_simple(RTM_GETADDR
, NLM_F_REQUEST
| NLM_F_DUMP
);
165 nlmsg_append(msg
, &ifa
, sizeof(ifa
), 0);
167 nl_send_auto_complete(rtnl_event
.sock
, msg
);
172 void ndp_handle_addr6_dump(void)
181 inline void ndp_rqs_addr6_dump(void)
186 int setup_ndp_interface(struct interface
*iface
, bool enable
)
189 bool dump_neigh
= false;
192 snprintf(procbuf
, sizeof(procbuf
), "/proc/sys/net/ipv6/conf/%s/proxy_ndp", iface
->ifname
);
193 procfd
= open(procbuf
, O_WRONLY
);
200 if (iface
->ndp_event
.uloop
.fd
> 0) {
201 uloop_fd_delete(&iface
->ndp_event
.uloop
);
202 close(iface
->ndp_event
.uloop
.fd
);
203 iface
->ndp_event
.uloop
.fd
= -1;
205 if (!enable
|| iface
->ndp
!= RELAYD_RELAY
)
206 if (write(procfd
, "0\n", 2) < 0) {}
211 if (enable
&& iface
->ndp
== RELAYD_RELAY
) {
212 if (write(procfd
, "1\n", 2) < 0) {}
214 int sock
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_CLOEXEC
, htons(ETH_P_IPV6
));
216 syslog(LOG_ERR
, "Unable to open packet socket: %s",
222 #ifdef PACKET_RECV_TYPE
223 int pktt
= 1 << PACKET_MULTICAST
;
224 setsockopt(sock
, SOL_PACKET
, PACKET_RECV_TYPE
, &pktt
, sizeof(pktt
));
227 if (setsockopt(sock
, SOL_SOCKET
, SO_ATTACH_FILTER
,
228 &bpf_prog
, sizeof(bpf_prog
))) {
229 syslog(LOG_ERR
, "Failed to set BPF: %s", strerror(errno
));
234 struct sockaddr_ll ll
= {
235 .sll_family
= AF_PACKET
,
236 .sll_ifindex
= iface
->ifindex
,
237 .sll_protocol
= htons(ETH_P_IPV6
),
243 bind(sock
, (struct sockaddr
*)&ll
, sizeof(ll
));
245 struct packet_mreq mreq
= {iface
->ifindex
, PACKET_MR_ALLMULTI
, ETH_ALEN
, {0}};
246 setsockopt(sock
, SOL_PACKET
, PACKET_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
));
248 iface
->ndp_event
.uloop
.fd
= sock
;
249 iface
->ndp_event
.handle_dgram
= handle_solicit
;
250 odhcpd_register(&iface
->ndp_event
);
252 // If we already were enabled dump is unnecessary, if not do dump
254 dump_neigh_table(false);
258 ndp_rqs_addr6_dump();
262 dump_neigh_table(true);
272 // Send an ICMP-ECHO. This is less for actually pinging but for the
273 // neighbor cache to be kept up-to-date.
274 static void ping6(struct in6_addr
*addr
,
275 const struct interface
*iface
)
277 struct sockaddr_in6 dest
= { .sin6_family
= AF_INET6
, .sin6_addr
= *addr
, .sin6_scope_id
= iface
->ifindex
, };
278 struct icmp6_hdr echo
= { .icmp6_type
= ICMP6_ECHO_REQUEST
};
279 struct iovec iov
= { .iov_base
= &echo
, .iov_len
= sizeof(echo
) };
280 char ipbuf
[INET6_ADDRSTRLEN
];
282 inet_ntop(AF_INET6
, addr
, ipbuf
, sizeof(ipbuf
));
283 syslog(LOG_NOTICE
, "Pinging for %s%%%s", ipbuf
, iface
->ifname
);
285 odhcpd_setup_route(addr
, 128, iface
, NULL
, 128, true);
286 odhcpd_send(ping_socket
, &dest
, &iov
, 1, iface
);
287 odhcpd_setup_route(addr
, 128, iface
, NULL
, 128, false);
290 // Handle solicitations
291 static void handle_solicit(void *addr
, void *data
, size_t len
,
292 struct interface
*iface
, _unused
void *dest
)
294 struct ip6_hdr
*ip6
= data
;
295 struct nd_neighbor_solicit
*req
= (struct nd_neighbor_solicit
*)&ip6
[1];
296 struct sockaddr_ll
*ll
= addr
;
297 char ipbuf
[INET6_ADDRSTRLEN
];
300 // Solicitation is for duplicate address detection
301 bool ns_is_dad
= IN6_IS_ADDR_UNSPECIFIED(&ip6
->ip6_src
);
303 // Don't process solicit messages on non relay interfaces
304 // Don't forward any non-DAD solicitation for external ifaces
305 // TODO: check if we should even forward DADs for them
306 if (iface
->ndp
!= RELAYD_RELAY
|| (iface
->external
&& !ns_is_dad
))
309 if (len
< sizeof(*ip6
) + sizeof(*req
))
310 return; // Invalid reqicitation
312 if (IN6_IS_ADDR_LINKLOCAL(&req
->nd_ns_target
) ||
313 IN6_IS_ADDR_LOOPBACK(&req
->nd_ns_target
) ||
314 IN6_IS_ADDR_MULTICAST(&req
->nd_ns_target
))
315 return; // Invalid target
317 inet_ntop(AF_INET6
, &req
->nd_ns_target
, ipbuf
, sizeof(ipbuf
));
318 syslog(LOG_DEBUG
, "Got a NS for %s%%%s", ipbuf
, iface
->ifname
);
320 odhcpd_get_mac(iface
, mac
);
321 if (!memcmp(ll
->sll_addr
, mac
, sizeof(mac
)))
322 return; // Looped back
325 list_for_each_entry(c
, &interfaces
, head
)
326 if (iface
!= c
&& c
->ndp
== RELAYD_RELAY
&&
327 (ns_is_dad
|| !c
->external
))
328 ping6(&req
->nd_ns_target
, c
);
331 // Use rtnetlink to modify kernel routes
332 static void setup_route(struct in6_addr
*addr
, struct interface
*iface
, bool add
)
334 char ipbuf
[INET6_ADDRSTRLEN
];
336 inet_ntop(AF_INET6
, addr
, ipbuf
, sizeof(ipbuf
));
337 syslog(LOG_NOTICE
, "%s about %s%%%s",
338 (add
) ? "Learned" : "Forgot", ipbuf
, iface
->ifname
);
340 if (iface
->learn_routes
)
341 odhcpd_setup_route(addr
, 128, iface
, NULL
, 1024, add
);
345 static int prefixcmp(const void *va
, const void *vb
)
347 const struct odhcpd_ipaddr
*a
= va
, *b
= vb
;
348 uint32_t a_pref
= IN6_IS_ADDR_ULA(&a
->addr
) ? 1 : a
->preferred
;
349 uint32_t b_pref
= IN6_IS_ADDR_ULA(&b
->addr
) ? 1 : b
->preferred
;
350 return (a_pref
< b_pref
) ? 1 : (a_pref
> b_pref
) ? -1 : 0;
353 // Check address update
354 static void check_addr_updates(struct interface
*iface
)
356 struct odhcpd_ipaddr addr
[RELAYD_MAX_ADDRS
] = {{IN6ADDR_ANY_INIT
, 0, 0, 0, 0}};
357 time_t now
= odhcpd_time();
358 ssize_t len
= odhcpd_get_interface_addresses(iface
->ifindex
, addr
, ARRAY_SIZE(addr
));
363 qsort(addr
, len
, sizeof(*addr
), prefixcmp
);
365 for (int i
= 0; i
< len
; ++i
) {
366 addr
[i
].addr
.s6_addr32
[3] = 0;
368 if (addr
[i
].preferred
< UINT32_MAX
- now
)
369 addr
[i
].preferred
+= now
;
371 if (addr
[i
].valid
< UINT32_MAX
- now
)
372 addr
[i
].valid
+= now
;
375 bool change
= len
!= (ssize_t
)iface
->ia_addr_len
;
376 for (ssize_t i
= 0; !change
&& i
< len
; ++i
)
377 if (!IN6_ARE_ADDR_EQUAL(&addr
[i
].addr
, &iface
->ia_addr
[i
].addr
) ||
378 (addr
[i
].preferred
> 0) != (iface
->ia_addr
[i
].preferred
> 0) ||
379 addr
[i
].valid
< iface
->ia_addr
[i
].valid
||
380 addr
[i
].preferred
< iface
->ia_addr
[i
].preferred
)
384 dhcpv6_ia_preupdate(iface
);
386 memcpy(iface
->ia_addr
, addr
, len
* sizeof(*addr
));
387 iface
->ia_addr_len
= len
;
390 dhcpv6_ia_postupdate(iface
, now
);
393 syslog(LOG_INFO
, "Raising SIGUSR1 due to address change on %s", iface
->ifname
);
398 void setup_addr_for_relaying(struct in6_addr
*addr
, struct interface
*iface
, bool add
)
401 char ipbuf
[INET6_ADDRSTRLEN
];
403 inet_ntop(AF_INET6
, addr
, ipbuf
, sizeof(ipbuf
));
405 list_for_each_entry(c
, &interfaces
, head
) {
406 if (iface
== c
|| (c
->ndp
!= RELAYD_RELAY
&& !add
))
409 add
= (c
->ndp
== RELAYD_RELAY
? add
: false);
411 if (odhcpd_setup_proxy_neigh(addr
, c
, add
))
412 syslog(LOG_DEBUG
, "Failed to %s proxy neighbour entry %s%%%s",
413 add
? "add" : "delete", ipbuf
, iface
->ifname
);
415 syslog(LOG_DEBUG
, "%s proxy neighbour entry %s%%%s",
416 add
? "Added" : "Deleted", ipbuf
, iface
->ifname
);
420 void setup_ping6(struct in6_addr
*addr
, struct interface
*iface
)
424 list_for_each_entry(c
, &interfaces
, head
) {
425 if (iface
== c
|| c
->ndp
!= RELAYD_RELAY
||
433 static struct in6_addr last_solicited
;
435 static void handle_rtnl_event(struct odhcpd_event
*e
)
437 struct event_socket
*ev_sock
= container_of(e
, struct event_socket
, ev
);
439 nl_recvmsgs_default(ev_sock
->sock
);
443 // Handler for neighbor cache entries from the kernel. This is our source
444 // to learn and unlearn hosts on interfaces.
445 static int cb_rtnl_valid(struct nl_msg
*msg
, _unused
void *arg
)
447 struct nlmsghdr
*hdr
= nlmsg_hdr(msg
);
448 struct in6_addr
*addr
= NULL
;
449 struct interface
*iface
= NULL
;
451 char ipbuf
[INET6_ADDRSTRLEN
];
453 switch (hdr
->nlmsg_type
) {
456 struct rtmsg
*rtm
= nlmsg_data(hdr
);
458 if (!nlmsg_valid_hdr(hdr
, sizeof(*rtm
)) ||
459 rtm
->rtm_family
!= AF_INET6
)
462 if (rtm
->rtm_dst_len
== 0) {
463 syslog(LOG_INFO
, "Raising SIGUSR1 due to default route change");
472 struct ifaddrmsg
*ifa
= nlmsg_data(hdr
);
473 struct nlattr
*nla
[__IFA_MAX
];
475 if (!nlmsg_valid_hdr(hdr
, sizeof(*ifa
)) ||
476 ifa
->ifa_family
!= AF_INET6
)
479 iface
= odhcpd_get_interface_by_index(ifa
->ifa_index
);
483 nlmsg_parse(hdr
, sizeof(*ifa
), nla
, __IFA_MAX
- 1, NULL
);
484 if (!nla
[IFA_ADDRESS
])
487 addr
= nla_data(nla
[IFA_ADDRESS
]);
488 if (!addr
|| IN6_IS_ADDR_LINKLOCAL(addr
) ||
489 IN6_IS_ADDR_MULTICAST(addr
))
492 inet_ntop(AF_INET6
, addr
, ipbuf
, sizeof(ipbuf
));
493 syslog(LOG_DEBUG
, "Netlink %s %s%%%s", true ? "newaddr" : "deladdr",
494 ipbuf
, iface
->ifname
);
496 check_addr_updates(iface
);
498 if (iface
->ndp
!= RELAYD_RELAY
)
501 /* handle the relay logic below */
502 setup_addr_for_relaying(addr
, iface
, add
);
505 dump_neigh_table(false);
512 struct ndmsg
*ndm
= nlmsg_data(hdr
);
513 struct nlattr
*nla
[__NDA_MAX
];
515 if (!nlmsg_valid_hdr(hdr
, sizeof(*ndm
)) ||
516 ndm
->ndm_family
!= AF_INET6
)
519 iface
= odhcpd_get_interface_by_index(ndm
->ndm_ifindex
);
520 if (!iface
|| iface
->ndp
!= RELAYD_RELAY
)
521 return (iface
? NL_OK
: NL_SKIP
);
523 nlmsg_parse(hdr
, sizeof(*ndm
), nla
, __NDA_MAX
- 1, NULL
);
527 addr
= nla_data(nla
[NDA_DST
]);
528 if (!addr
|| IN6_IS_ADDR_LINKLOCAL(addr
) ||
529 IN6_IS_ADDR_MULTICAST(addr
))
532 inet_ntop(AF_INET6
, addr
, ipbuf
, sizeof(ipbuf
));
533 syslog(LOG_DEBUG
, "Netlink %s %s%%%s", true ? "newneigh" : "delneigh",
534 ipbuf
, iface
->ifname
);
536 if (ndm
->ndm_flags
& NTF_PROXY
) {
537 /* Dump and flush proxy entries */
538 if (hdr
->nlmsg_type
== RTM_NEWNEIGH
) {
539 odhcpd_setup_proxy_neigh(addr
, iface
, false);
540 setup_route(addr
, iface
, false);
541 dump_neigh_table(false);
547 if (add
&& !(ndm
->ndm_state
&
548 (NUD_REACHABLE
| NUD_STALE
| NUD_DELAY
| NUD_PROBE
|
549 NUD_PERMANENT
| NUD_NOARP
))) {
550 if (!IN6_ARE_ADDR_EQUAL(&last_solicited
, addr
)) {
551 last_solicited
= *addr
;
552 setup_ping6(addr
, iface
);
558 setup_addr_for_relaying(addr
, iface
, add
);
559 setup_route(addr
, iface
, add
);
562 dump_neigh_table(false);
573 static void catch_rtnl_err(struct odhcpd_event
*e
, int error
)
575 struct event_socket
*ev_sock
= container_of(e
, struct event_socket
, ev
);
577 if (error
!= ENOBUFS
)
580 /* Double netlink event buffer size */
581 ev_sock
->sock_bufsize
*= 2;
583 if (nl_socket_set_buffer_size(ev_sock
->sock
, ev_sock
->sock_bufsize
, 0))
590 odhcpd_deregister(e
);