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