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