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