Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/arp/arp_netlink.c
104792 views
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <stdbool.h>
5
#include <errno.h>
6
#include <netdb.h>
7
8
#include <sys/bitcount.h>
9
#include <sys/param.h>
10
#include <sys/linker.h>
11
#include <sys/module.h>
12
#include <sys/socket.h>
13
#include <sys/sysctl.h>
14
#include <sys/time.h>
15
#include <sys/types.h>
16
17
#include <netinet/in.h>
18
#include <arpa/inet.h>
19
20
#include <net/ethernet.h>
21
#include <net/if.h>
22
#include <net/if_dl.h>
23
#include <net/if_types.h>
24
#include <netlink/netlink.h>
25
#include <netlink/netlink_route.h>
26
#include <netlink/netlink_snl.h>
27
#include <netlink/netlink_snl_route.h>
28
#include <netlink/netlink_snl_route_compat.h>
29
#include <netlink/netlink_snl_route_parsers.h>
30
31
#include <libxo/xo.h>
32
#include "arp.h"
33
34
#define RTF_ANNOUNCE RTF_PROTO2
35
36
static void
37
nl_init_socket(struct snl_state *ss)
38
{
39
if (snl_init(ss, NETLINK_ROUTE))
40
return;
41
42
if (modfind("netlink") == -1 && errno == ENOENT) {
43
/* Try to load */
44
if (kldload("netlink") == -1)
45
xo_err(1, "netlink is not loaded and load attempt failed");
46
if (snl_init(ss, NETLINK_ROUTE))
47
return;
48
}
49
50
xo_err(1, "unable to open netlink socket");
51
}
52
53
static bool
54
get_link_info(struct snl_state *ss, uint32_t ifindex,
55
struct snl_parsed_link_simple *link)
56
{
57
struct snl_writer nw;
58
59
snl_init_writer(ss, &nw);
60
61
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
62
struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
63
if (ifmsg != NULL)
64
ifmsg->ifi_index = ifindex;
65
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
66
return (false);
67
68
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
69
70
if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
71
return (false);
72
73
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
74
return (false);
75
76
return (true);
77
}
78
79
80
81
static bool
82
has_l2(struct snl_state *ss, uint32_t ifindex, uint32_t *pflags)
83
{
84
struct snl_parsed_link_simple link = {};
85
86
*pflags = 0;
87
if (!get_link_info(ss, ifindex, &link))
88
return (false);
89
90
*pflags = link.ifi_flags;
91
return (valid_type(link.ifi_type) != 0);
92
}
93
94
static uint32_t
95
get_myfib(void)
96
{
97
uint32_t fibnum = 0;
98
size_t len = sizeof(fibnum);
99
100
sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
101
102
return (fibnum);
103
}
104
105
static int
106
guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr)
107
{
108
struct snl_writer nw;
109
uint32_t ifindex, ifflags;
110
111
snl_init_writer(ss, &nw);
112
113
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
114
struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
115
rtm->rtm_family = AF_INET;
116
117
struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr };
118
snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)&dst);
119
snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
120
121
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
122
return (0);
123
124
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
125
126
if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
127
/* No route found, unable to guess ifindex */
128
return (0);
129
}
130
131
struct snl_parsed_route r = {};
132
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
133
return (0);
134
135
if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
136
return (0);
137
138
/* Check if the interface is of supported type */
139
if (has_l2(ss, r.rta_oif, &ifflags))
140
return (r.rta_oif);
141
142
/* Check if we are doing proxy arp for P2P interface */
143
if (ifflags & IFF_POINTOPOINT) {
144
/* Guess interface by dst prefix */
145
if (get_ifinfo(addr.s_addr, NULL, &ifindex))
146
return (ifindex);
147
}
148
149
/* Check the case when we matched the loopback route for P2P */
150
snl_init_writer(ss, &nw);
151
hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
152
snl_reserve_msg_object(&nw, struct nhmsg);
153
154
int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
155
snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
156
snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET);
157
snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
158
snl_end_attr_nested(&nw, off);
159
160
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
161
return (0);
162
163
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
164
165
if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
166
/* No nexthop found, unable to guess ifindex */
167
return (0);
168
}
169
170
struct snl_parsed_nhop nh = {};
171
if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
172
return (0);
173
174
return (nh.nhaf_aif);
175
}
176
177
static uint32_t
178
fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr)
179
{
180
if (ifindex == 0)
181
ifindex = guess_ifindex(ss, get_myfib(), addr);
182
return (ifindex);
183
}
184
185
static void
186
print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
187
{
188
const char *host;
189
struct hostent *hp;
190
struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst;
191
192
xo_open_instance("arp-cache");
193
194
if (!opts.nflag)
195
hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
196
sizeof(addr->sin_addr), AF_INET);
197
else
198
hp = 0;
199
if (hp)
200
host = hp->h_name;
201
else {
202
host = "?";
203
if (h_errno == TRY_AGAIN)
204
opts.nflag = true;
205
}
206
xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
207
inet_ntoa(addr->sin_addr));
208
if (neigh->nda_lladdr != NULL) {
209
struct sockaddr_dl sdl = {
210
.sdl_family = AF_LINK,
211
.sdl_type = link->ifi_type,
212
.sdl_len = sizeof(struct sockaddr_dl),
213
.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
214
};
215
memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
216
217
if ((sdl.sdl_type == IFT_ETHER ||
218
sdl.sdl_type == IFT_L2VLAN ||
219
sdl.sdl_type == IFT_BRIDGE) &&
220
sdl.sdl_alen == ETHER_ADDR_LEN)
221
xo_emit("{:mac-address/%s}",
222
ether_ntoa((struct ether_addr *)LLADDR(&sdl)));
223
else {
224
225
xo_emit("{:mac-address/%s}", link_ntoa(&sdl));
226
}
227
} else
228
xo_emit("{d:/(incomplete)}{en:incomplete/true}");
229
xo_emit(" on {:interface/%s}", link->ifla_ifname);
230
231
if (neigh->ndaf_next_ts == 0)
232
xo_emit("{d:/ permanent}{en:permanent/true}");
233
else {
234
time_t expire_time;
235
struct timeval now;
236
237
gettimeofday(&now, 0);
238
if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0)
239
xo_emit(" expires in {:expires/%d} seconds",
240
(int)expire_time);
241
else
242
xo_emit("{d:/ expired}{en:expired/true}");
243
}
244
245
if (neigh->ndm_flags & NTF_PROXY)
246
xo_emit("{d:/ published}{en:published/true}");
247
248
switch(link->ifi_type) {
249
case IFT_ETHER:
250
xo_emit(" [{:type/ethernet}]");
251
break;
252
case IFT_FDDI:
253
xo_emit(" [{:type/fddi}]");
254
break;
255
case IFT_ATM:
256
xo_emit(" [{:type/atm}]");
257
break;
258
case IFT_L2VLAN:
259
xo_emit(" [{:type/vlan}]");
260
break;
261
case IFT_IEEE1394:
262
xo_emit(" [{:type/firewire}]");
263
break;
264
case IFT_BRIDGE:
265
xo_emit(" [{:type/bridge}]");
266
break;
267
case IFT_INFINIBAND:
268
xo_emit(" [{:type/infiniband}]");
269
break;
270
default:
271
break;
272
}
273
274
xo_emit("\n");
275
276
xo_close_instance("arp-cache");
277
}
278
279
int
280
print_entries_nl(uint32_t ifindex, struct in_addr addr)
281
{
282
struct snl_state ss_req = {}, ss_cmd = {};
283
struct snl_parsed_link_simple link = {};
284
struct snl_writer nw;
285
286
nl_init_socket(&ss_req);
287
snl_init_writer(&ss_req, &nw);
288
289
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
290
struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
291
if (ndmsg != NULL) {
292
ndmsg->ndm_family = AF_INET;
293
/* let kernel filter results by interface if provided */
294
ndmsg->ndm_ifindex = ifindex;
295
}
296
297
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) {
298
snl_free(&ss_req);
299
return (0);
300
}
301
302
uint32_t nlmsg_seq = hdr->nlmsg_seq;
303
struct snl_errmsg_data e = {};
304
int count = 0;
305
nl_init_socket(&ss_cmd);
306
307
while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
308
struct snl_parsed_neigh neigh = {};
309
struct sockaddr_in *neighaddr;
310
311
if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
312
continue;
313
314
if (neigh.nda_ifindex != link.ifi_index) {
315
snl_clear_lb(&ss_cmd);
316
memset(&link, 0, sizeof(link));
317
if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
318
continue;
319
}
320
321
/* filter results based on host if provided */
322
neighaddr = (struct sockaddr_in *)neigh.nda_dst;
323
if (addr.s_addr &&
324
(addr.s_addr != neighaddr->sin_addr.s_addr))
325
continue;
326
327
print_entry(&neigh, &link);
328
count++;
329
snl_clear_lb(&ss_req);
330
}
331
332
snl_free(&ss_req);
333
snl_free(&ss_cmd);
334
335
return (count);
336
}
337
338
int
339
delete_nl(char *host)
340
{
341
struct snl_state ss = {};
342
struct snl_writer nw;
343
struct sockaddr_in *dst;
344
uint32_t ifindex = opts.rifindex;
345
346
dst = getaddr(host);
347
if (dst == NULL)
348
return (1);
349
350
nl_init_socket(&ss);
351
352
ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
353
if (ifindex == 0) {
354
xo_warnx("delete: cannot locate %s", host);
355
snl_free(&ss);
356
return (0);
357
}
358
359
snl_init_writer(&ss, &nw);
360
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
361
struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
362
if (ndmsg != NULL) {
363
ndmsg->ndm_family = AF_INET;
364
ndmsg->ndm_ifindex = ifindex;
365
}
366
snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
367
368
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
369
snl_free(&ss);
370
return (1);
371
}
372
373
struct snl_errmsg_data e = {};
374
snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
375
if (e.error != 0) {
376
if (e.error_str != NULL)
377
xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
378
else
379
xo_warnx("delete %s: %s", host, strerror(e.error));
380
} else
381
printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr));
382
383
snl_free(&ss);
384
385
return (e.error != 0);
386
}
387
388
int
389
set_nl(struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host)
390
{
391
struct snl_state ss = {};
392
struct snl_writer nw;
393
uint32_t ifindex = opts.rifindex;
394
395
nl_init_socket(&ss);
396
397
ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
398
if (ifindex == 0) {
399
xo_warnx("set: cannot locate %s", host);
400
snl_free(&ss);
401
return (0);
402
}
403
404
snl_init_writer(&ss, &nw);
405
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
406
hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
407
struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
408
if (ndmsg != NULL) {
409
uint8_t nl_flags = 0;
410
411
ndmsg->ndm_family = AF_INET;
412
ndmsg->ndm_ifindex = ifindex;
413
ndmsg->ndm_state = (opts.expire_time == 0) ? \
414
NUD_PERMANENT : NUD_NONE;
415
416
if (opts.flags & RTF_ANNOUNCE)
417
nl_flags |= NTF_PROXY;
418
if (opts.expire_time == 0)
419
nl_flags |= NTF_STICKY;
420
ndmsg->ndm_flags = nl_flags;
421
}
422
snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
423
snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
424
425
if (opts.expire_time != 0) {
426
struct timeval now;
427
428
gettimeofday(&now, 0);
429
int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD);
430
snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time);
431
snl_end_attr_nested(&nw, off);
432
}
433
434
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
435
snl_free(&ss);
436
return (1);
437
}
438
439
struct snl_errmsg_data e = {};
440
snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
441
if (e.error != 0) {
442
if (e.error_str != NULL)
443
xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
444
else
445
xo_warnx("set %s: %s", host, strerror(e.error));
446
}
447
snl_free(&ss);
448
449
return (e.error != 0);
450
}
451
452
453