]>
Commit | Line | Data |
---|---|---|
2fb975da TT |
1 | /* NHRP netlink/neighbor table arpd code |
2 | * Copyright (c) 2014-2016 Timo Teräs | |
3 | * | |
4 | * This file is free software: you may copy, redistribute and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation, either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | */ | |
9 | ||
b45ac5f5 DL |
10 | #ifdef HAVE_CONFIG_H |
11 | #include "config.h" | |
12 | #endif | |
13 | ||
2fb975da TT |
14 | #include <fcntl.h> |
15 | #include <net/if.h> | |
16 | #include <netinet/if_ether.h> | |
17 | #include <linux/netlink.h> | |
18 | #include <linux/neighbour.h> | |
19 | #include <linux/netfilter/nfnetlink_log.h> | |
20 | ||
21 | #include "thread.h" | |
22 | #include "nhrpd.h" | |
23 | #include "netlink.h" | |
24 | #include "znl.h" | |
25 | ||
26 | int netlink_req_fd = -1; | |
27 | int netlink_nflog_group; | |
28 | static int netlink_log_fd = -1; | |
29 | static struct thread *netlink_log_thread; | |
30 | static int netlink_listen_fd = -1; | |
31 | ||
32 | typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb); | |
33 | ||
996c9314 LB |
34 | void netlink_update_binding(struct interface *ifp, union sockunion *proto, |
35 | union sockunion *nbma) | |
2fb975da TT |
36 | { |
37 | struct nlmsghdr *n; | |
38 | struct ndmsg *ndm; | |
39 | struct zbuf *zb = zbuf_alloc(512); | |
40 | ||
996c9314 LB |
41 | n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, |
42 | NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); | |
2fb975da | 43 | ndm = znl_push(zb, sizeof(*ndm)); |
996c9314 | 44 | *ndm = (struct ndmsg){ |
2fb975da TT |
45 | .ndm_family = sockunion_family(proto), |
46 | .ndm_ifindex = ifp->ifindex, | |
47 | .ndm_type = RTN_UNICAST, | |
48 | .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED, | |
49 | }; | |
996c9314 LB |
50 | znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), |
51 | family2addrsize(sockunion_family(proto))); | |
2fb975da | 52 | if (nbma) |
996c9314 LB |
53 | znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), |
54 | family2addrsize(sockunion_family(nbma))); | |
2fb975da TT |
55 | znl_nlmsg_complete(zb, n); |
56 | zbuf_send(zb, netlink_req_fd); | |
57 | zbuf_recv(zb, netlink_req_fd); | |
58 | zbuf_free(zb); | |
59 | } | |
60 | ||
61 | static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) | |
62 | { | |
63 | struct ndmsg *ndm; | |
64 | struct rtattr *rta; | |
65 | struct nhrp_cache *c; | |
66 | struct interface *ifp; | |
67 | struct zbuf payload; | |
e9178b8b | 68 | union sockunion addr, lladdr; |
2fb975da | 69 | size_t len; |
2fb975da TT |
70 | int state; |
71 | ||
5f36c26c | 72 | memset(&lladdr, 0, sizeof(lladdr)); |
2fb975da | 73 | ndm = znl_pull(zb, sizeof(*ndm)); |
996c9314 LB |
74 | if (!ndm) |
75 | return; | |
2fb975da TT |
76 | |
77 | sockunion_family(&addr) = AF_UNSPEC; | |
78 | while ((rta = znl_rta_pull(zb, &payload)) != NULL) { | |
79 | len = zbuf_used(&payload); | |
80 | switch (rta->rta_type) { | |
81 | case NDA_DST: | |
996c9314 LB |
82 | sockunion_set(&addr, ndm->ndm_family, |
83 | zbuf_pulln(&payload, len), len); | |
2fb975da | 84 | break; |
e9178b8b GN |
85 | case NDA_LLADDR: |
86 | sockunion_set(&lladdr, ndm->ndm_family, | |
87 | zbuf_pulln(&payload, len), len); | |
88 | break; | |
2fb975da TT |
89 | } |
90 | } | |
91 | ||
7e2b7603 | 92 | ifp = if_lookup_by_index(ndm->ndm_ifindex, VRF_DEFAULT); |
2fb975da TT |
93 | if (!ifp || sockunion_family(&addr) == AF_UNSPEC) |
94 | return; | |
95 | ||
96 | c = nhrp_cache_get(ifp, &addr, 0); | |
97 | if (!c) | |
98 | return; | |
99 | ||
e9178b8b | 100 | debugf(NHRP_DEBUG_KERNEL, |
b6c48481 | 101 | "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", |
e9178b8b GN |
102 | (msg->nlmsg_type == RTM_GETNEIGH) |
103 | ? "who-has" | |
104 | : (msg->nlmsg_type == RTM_NEWNEIGH) ? "new-neigh" | |
105 | : "del-neigh", | |
b6c48481 | 106 | &addr, ifp->name, &lladdr, ndm->ndm_state, c->used, c->cur.type); |
2fb975da | 107 | |
e9178b8b | 108 | if (msg->nlmsg_type == RTM_GETNEIGH) { |
2fb975da TT |
109 | if (c->cur.type >= NHRP_CACHE_CACHED) { |
110 | nhrp_cache_set_used(c, 1); | |
e9178b8b | 111 | debugf(NHRP_DEBUG_KERNEL, |
b6c48481 DS |
112 | "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", |
113 | &addr, ifp->name, &c->cur.remote_nbma_natoa, | |
114 | &c->cur.peer->vc->remote.nbma, &lladdr); | |
e9178b8b GN |
115 | /* In case of shortcuts, nbma is given by lladdr, not |
116 | * vc->remote.nbma. | |
117 | */ | |
118 | netlink_update_binding(ifp, &addr, &lladdr); | |
2fb975da TT |
119 | } |
120 | } else { | |
996c9314 LB |
121 | state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state |
122 | : NUD_FAILED; | |
2fb975da TT |
123 | nhrp_cache_set_used(c, state == NUD_REACHABLE); |
124 | } | |
125 | } | |
126 | ||
127 | static int netlink_route_recv(struct thread *t) | |
128 | { | |
129 | uint8_t buf[ZNL_BUFFER_SIZE]; | |
130 | int fd = THREAD_FD(t); | |
131 | struct zbuf payload, zb; | |
132 | struct nlmsghdr *n; | |
133 | ||
134 | zbuf_init(&zb, buf, sizeof(buf), 0); | |
135 | while (zbuf_recv(&zb, fd) > 0) { | |
831600c3 | 136 | while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) { |
996c9314 LB |
137 | debugf(NHRP_DEBUG_KERNEL, |
138 | "Netlink: Received msg_type %u, msg_flags %u", | |
139 | n->nlmsg_type, n->nlmsg_flags); | |
2fb975da TT |
140 | switch (n->nlmsg_type) { |
141 | case RTM_GETNEIGH: | |
142 | case RTM_NEWNEIGH: | |
143 | case RTM_DELNEIGH: | |
144 | netlink_neigh_msg(n, &payload); | |
145 | break; | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
ffa2c898 | 150 | thread_add_read(master, netlink_route_recv, 0, fd, NULL); |
2fb975da TT |
151 | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static void netlink_log_register(int fd, int group) | |
156 | { | |
157 | struct nlmsghdr *n; | |
158 | struct nfgenmsg *nf; | |
159 | struct nfulnl_msg_config_cmd cmd; | |
160 | struct zbuf *zb = zbuf_alloc(512); | |
161 | ||
996c9314 LB |
162 | n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG, |
163 | NLM_F_REQUEST | NLM_F_ACK); | |
2fb975da | 164 | nf = znl_push(zb, sizeof(*nf)); |
996c9314 | 165 | *nf = (struct nfgenmsg){ |
2fb975da TT |
166 | .nfgen_family = AF_UNSPEC, |
167 | .version = NFNETLINK_V0, | |
168 | .res_id = htons(group), | |
169 | }; | |
170 | cmd.command = NFULNL_CFG_CMD_BIND; | |
171 | znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd)); | |
172 | znl_nlmsg_complete(zb, n); | |
173 | ||
174 | zbuf_send(zb, fd); | |
175 | zbuf_free(zb); | |
176 | } | |
177 | ||
178 | static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb) | |
179 | { | |
180 | struct nfgenmsg *nf; | |
181 | struct rtattr *rta; | |
182 | struct zbuf rtapl, pktpl; | |
183 | struct interface *ifp; | |
184 | struct nfulnl_msg_packet_hdr *pkthdr = NULL; | |
185 | uint32_t *in_ndx = NULL; | |
186 | ||
187 | nf = znl_pull(zb, sizeof(*nf)); | |
996c9314 LB |
188 | if (!nf) |
189 | return; | |
2fb975da TT |
190 | |
191 | memset(&pktpl, 0, sizeof(pktpl)); | |
192 | while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) { | |
193 | switch (rta->rta_type) { | |
194 | case NFULA_PACKET_HDR: | |
195 | pkthdr = znl_pull(&rtapl, sizeof(*pkthdr)); | |
196 | break; | |
197 | case NFULA_IFINDEX_INDEV: | |
198 | in_ndx = znl_pull(&rtapl, sizeof(*in_ndx)); | |
199 | break; | |
200 | case NFULA_PAYLOAD: | |
201 | pktpl = rtapl; | |
202 | break; | |
996c9314 LB |
203 | /* NFULA_HWHDR exists and is supposed to contain source |
204 | * hardware address. However, for ip_gre it seems to be | |
205 | * the nexthop destination address if the packet matches | |
206 | * route. */ | |
2fb975da TT |
207 | } |
208 | } | |
209 | ||
210 | if (!pkthdr || !in_ndx || !zbuf_used(&pktpl)) | |
211 | return; | |
212 | ||
7e2b7603 | 213 | ifp = if_lookup_by_index(htonl(*in_ndx), VRF_DEFAULT); |
2fb975da TT |
214 | if (!ifp) |
215 | return; | |
216 | ||
217 | nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl); | |
218 | } | |
219 | ||
220 | static int netlink_log_recv(struct thread *t) | |
221 | { | |
222 | uint8_t buf[ZNL_BUFFER_SIZE]; | |
223 | int fd = THREAD_FD(t); | |
224 | struct zbuf payload, zb; | |
225 | struct nlmsghdr *n; | |
226 | ||
227 | netlink_log_thread = NULL; | |
228 | ||
229 | zbuf_init(&zb, buf, sizeof(buf), 0); | |
230 | while (zbuf_recv(&zb, fd) > 0) { | |
831600c3 | 231 | while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) { |
996c9314 LB |
232 | debugf(NHRP_DEBUG_KERNEL, |
233 | "Netlink-log: Received msg_type %u, msg_flags %u", | |
234 | n->nlmsg_type, n->nlmsg_flags); | |
2fb975da | 235 | switch (n->nlmsg_type) { |
996c9314 | 236 | case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET: |
2fb975da TT |
237 | netlink_log_indication(n, &payload); |
238 | break; | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
ffa2c898 QY |
243 | thread_add_read(master, netlink_log_recv, 0, netlink_log_fd, |
244 | &netlink_log_thread); | |
2fb975da TT |
245 | |
246 | return 0; | |
247 | } | |
248 | ||
249 | void netlink_set_nflog_group(int nlgroup) | |
250 | { | |
251 | if (netlink_log_fd >= 0) { | |
50478845 | 252 | thread_cancel(&netlink_log_thread); |
2fb975da TT |
253 | close(netlink_log_fd); |
254 | netlink_log_fd = -1; | |
255 | } | |
256 | netlink_nflog_group = nlgroup; | |
257 | if (nlgroup) { | |
996c9314 | 258 | netlink_log_fd = znl_open(NETLINK_NETFILTER, 0); |
6c8ca260 JB |
259 | if (netlink_log_fd < 0) |
260 | return; | |
261 | ||
2fb975da | 262 | netlink_log_register(netlink_log_fd, nlgroup); |
ffa2c898 QY |
263 | thread_add_read(master, netlink_log_recv, 0, netlink_log_fd, |
264 | &netlink_log_thread); | |
2fb975da TT |
265 | } |
266 | } | |
267 | ||
6c8ca260 | 268 | void netlink_init(void) |
2fb975da TT |
269 | { |
270 | netlink_req_fd = znl_open(NETLINK_ROUTE, 0); | |
6c8ca260 JB |
271 | if (netlink_req_fd < 0) |
272 | return; | |
273 | ||
2fb975da | 274 | netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH); |
6c8ca260 JB |
275 | if (netlink_listen_fd < 0) |
276 | return; | |
277 | ||
996c9314 | 278 | thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd, NULL); |
2fb975da TT |
279 | } |
280 | ||
281 | int netlink_configure_arp(unsigned int ifindex, int pf) | |
282 | { | |
283 | struct nlmsghdr *n; | |
284 | struct ndtmsg *ndtm; | |
285 | struct rtattr *rta; | |
286 | struct zbuf *zb = zbuf_alloc(512); | |
287 | int r; | |
288 | ||
289 | n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE); | |
290 | ndtm = znl_push(zb, sizeof(*ndtm)); | |
996c9314 | 291 | *ndtm = (struct ndtmsg){ |
2fb975da TT |
292 | .ndtm_family = pf, |
293 | }; | |
294 | ||
996c9314 LB |
295 | znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", |
296 | 10); | |
2fb975da TT |
297 | |
298 | rta = znl_rta_nested_push(zb, NDTA_PARMS); | |
299 | znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex); | |
300 | znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1); | |
301 | znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0); | |
302 | znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0); | |
303 | znl_rta_nested_complete(zb, rta); | |
304 | ||
305 | znl_nlmsg_complete(zb, n); | |
306 | r = zbuf_send(zb, netlink_req_fd); | |
307 | zbuf_recv(zb, netlink_req_fd); | |
308 | zbuf_free(zb); | |
309 | ||
310 | return r; | |
311 | } |