]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/netlink_arp.c
zebra: link layer config and notification, implementation in zebra
[mirror_frr.git] / nhrpd / netlink_arp.c
CommitLineData
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
26int netlink_req_fd = -1;
27int netlink_nflog_group;
28static int netlink_log_fd = -1;
29static struct thread *netlink_log_thread;
30static int netlink_listen_fd = -1;
31
32typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb);
33
996c9314
LB
34void 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
61static 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
127static 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
155static 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
178static 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
220static 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
249void 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 268void 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
281int 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}