1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <errno.h> |
4 | #include <error.h> |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
7 | #include <string.h> |
8 | #include <unistd.h> |
9 | #include <limits.h> |
10 | |
11 | #include <sys/socket.h> |
12 | #include <sys/types.h> |
13 | |
14 | #include <arpa/inet.h> |
15 | #include <net/if.h> |
16 | |
17 | #include <linux/rtnetlink.h> |
18 | #include <linux/genetlink.h> |
19 | |
20 | #include "linux/mptcp.h" |
21 | |
22 | #ifndef MPTCP_PM_NAME |
23 | #define MPTCP_PM_NAME "mptcp_pm" |
24 | #endif |
25 | #ifndef MPTCP_PM_EVENTS |
26 | #define MPTCP_PM_EVENTS "mptcp_pm_events" |
27 | #endif |
28 | #ifndef IPPROTO_MPTCP |
29 | #define IPPROTO_MPTCP 262 |
30 | #endif |
31 | |
32 | static void syntax(char *argv[]) |
33 | { |
34 | fprintf(stderr, "%s add|ann|rem|csf|dsf|get|set|del|flush|dump|events|listen|accept [<args>]\n" , argv[0]); |
35 | fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh] [id <nr>] [dev <name>] <ip>\n" ); |
36 | fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>]\n" ); |
37 | fprintf(stderr, "\trem id <local-id> token <token>\n" ); |
38 | fprintf(stderr, "\tcsf lip <local-ip> lid <local-id> rip <remote-ip> rport <remote-port> token <token>\n" ); |
39 | fprintf(stderr, "\tdsf lip <local-ip> lport <local-port> rip <remote-ip> rport <remote-port> token <token>\n" ); |
40 | fprintf(stderr, "\tdel <id> [<ip>]\n" ); |
41 | fprintf(stderr, "\tget <id>\n" ); |
42 | fprintf(stderr, "\tset [<ip>] [id <nr>] flags [no]backup|[no]fullmesh [port <nr>] [token <token>] [rip <ip>] [rport <port>]\n" ); |
43 | fprintf(stderr, "\tflush\n" ); |
44 | fprintf(stderr, "\tdump\n" ); |
45 | fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n" ); |
46 | fprintf(stderr, "\tevents\n" ); |
47 | fprintf(stderr, "\tlisten <local-ip> <local-port>\n" ); |
48 | exit(0); |
49 | } |
50 | |
51 | static int init_genl_req(char *data, int family, int cmd, int version) |
52 | { |
53 | struct nlmsghdr *nh = (void *)data; |
54 | struct genlmsghdr *gh; |
55 | int off = 0; |
56 | |
57 | nh->nlmsg_type = family; |
58 | nh->nlmsg_flags = NLM_F_REQUEST; |
59 | nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); |
60 | off += NLMSG_ALIGN(sizeof(*nh)); |
61 | |
62 | gh = (void *)(data + off); |
63 | gh->cmd = cmd; |
64 | gh->version = version; |
65 | off += NLMSG_ALIGN(sizeof(*gh)); |
66 | return off; |
67 | } |
68 | |
69 | static int nl_error(struct nlmsghdr *nh) |
70 | { |
71 | struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh); |
72 | int len = nh->nlmsg_len - sizeof(*nh); |
73 | uint32_t off; |
74 | |
75 | if (len < sizeof(struct nlmsgerr)) { |
76 | error(1, 0, "netlink error message truncated %d min %ld" , len, |
77 | sizeof(struct nlmsgerr)); |
78 | return -1; |
79 | } |
80 | |
81 | if (err->error) { |
82 | /* check messages from kernel */ |
83 | struct rtattr *attrs = (struct rtattr *)NLMSG_DATA(nh); |
84 | |
85 | fprintf(stderr, "netlink error %d (%s)\n" , |
86 | err->error, strerror(-err->error)); |
87 | |
88 | while (RTA_OK(attrs, len)) { |
89 | if (attrs->rta_type == NLMSGERR_ATTR_MSG) |
90 | fprintf(stderr, "netlink ext ack msg: %s\n" , |
91 | (char *)RTA_DATA(attrs)); |
92 | if (attrs->rta_type == NLMSGERR_ATTR_OFFS) { |
93 | memcpy(&off, RTA_DATA(attrs), 4); |
94 | fprintf(stderr, "netlink err off %d\n" , |
95 | (int)off); |
96 | } |
97 | attrs = RTA_NEXT(attrs, len); |
98 | } |
99 | return -1; |
100 | } |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static int capture_events(int fd, int event_group) |
106 | { |
107 | u_int8_t buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
108 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024]; |
109 | struct genlmsghdr *ghdr; |
110 | struct rtattr *attrs; |
111 | struct nlmsghdr *nh; |
112 | int ret = 0; |
113 | int res_len; |
114 | int msg_len; |
115 | fd_set rfds; |
116 | |
117 | if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, |
118 | &event_group, sizeof(event_group)) < 0) |
119 | error(1, errno, "could not join the " MPTCP_PM_EVENTS " mcast group" ); |
120 | |
121 | do { |
122 | FD_ZERO(&rfds); |
123 | FD_SET(fd, &rfds); |
124 | res_len = NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
125 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024; |
126 | |
127 | ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL); |
128 | |
129 | if (ret < 0) |
130 | error(1, ret, "error in select() on NL socket" ); |
131 | |
132 | res_len = recv(fd, buffer, res_len, 0); |
133 | if (res_len < 0) |
134 | error(1, res_len, "error on recv() from NL socket" ); |
135 | |
136 | nh = (struct nlmsghdr *)buffer; |
137 | |
138 | for (; NLMSG_OK(nh, res_len); nh = NLMSG_NEXT(nh, res_len)) { |
139 | if (nh->nlmsg_type == NLMSG_ERROR) |
140 | error(1, NLMSG_ERROR, "received invalid NL message" ); |
141 | |
142 | ghdr = (struct genlmsghdr *)NLMSG_DATA(nh); |
143 | |
144 | if (ghdr->cmd == 0) |
145 | continue; |
146 | |
147 | fprintf(stderr, "type:%d" , ghdr->cmd); |
148 | |
149 | msg_len = nh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); |
150 | |
151 | attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); |
152 | while (RTA_OK(attrs, msg_len)) { |
153 | if (attrs->rta_type == MPTCP_ATTR_TOKEN) |
154 | fprintf(stderr, ",token:%u" , *(__u32 *)RTA_DATA(attrs)); |
155 | else if (attrs->rta_type == MPTCP_ATTR_FAMILY) |
156 | fprintf(stderr, ",family:%u" , *(__u16 *)RTA_DATA(attrs)); |
157 | else if (attrs->rta_type == MPTCP_ATTR_LOC_ID) |
158 | fprintf(stderr, ",loc_id:%u" , *(__u8 *)RTA_DATA(attrs)); |
159 | else if (attrs->rta_type == MPTCP_ATTR_REM_ID) |
160 | fprintf(stderr, ",rem_id:%u" , *(__u8 *)RTA_DATA(attrs)); |
161 | else if (attrs->rta_type == MPTCP_ATTR_SADDR4) { |
162 | u_int32_t saddr4 = ntohl(*(__u32 *)RTA_DATA(attrs)); |
163 | |
164 | fprintf(stderr, ",saddr4:%u.%u.%u.%u" , saddr4 >> 24, |
165 | (saddr4 >> 16) & 0xFF, (saddr4 >> 8) & 0xFF, |
166 | (saddr4 & 0xFF)); |
167 | } else if (attrs->rta_type == MPTCP_ATTR_SADDR6) { |
168 | char buf[INET6_ADDRSTRLEN]; |
169 | |
170 | if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf, |
171 | sizeof(buf)) != NULL) |
172 | fprintf(stderr, ",saddr6:%s" , buf); |
173 | } else if (attrs->rta_type == MPTCP_ATTR_DADDR4) { |
174 | u_int32_t daddr4 = ntohl(*(__u32 *)RTA_DATA(attrs)); |
175 | |
176 | fprintf(stderr, ",daddr4:%u.%u.%u.%u" , daddr4 >> 24, |
177 | (daddr4 >> 16) & 0xFF, (daddr4 >> 8) & 0xFF, |
178 | (daddr4 & 0xFF)); |
179 | } else if (attrs->rta_type == MPTCP_ATTR_DADDR6) { |
180 | char buf[INET6_ADDRSTRLEN]; |
181 | |
182 | if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf, |
183 | sizeof(buf)) != NULL) |
184 | fprintf(stderr, ",daddr6:%s" , buf); |
185 | } else if (attrs->rta_type == MPTCP_ATTR_SPORT) |
186 | fprintf(stderr, ",sport:%u" , |
187 | ntohs(*(__u16 *)RTA_DATA(attrs))); |
188 | else if (attrs->rta_type == MPTCP_ATTR_DPORT) |
189 | fprintf(stderr, ",dport:%u" , |
190 | ntohs(*(__u16 *)RTA_DATA(attrs))); |
191 | else if (attrs->rta_type == MPTCP_ATTR_BACKUP) |
192 | fprintf(stderr, ",backup:%u" , *(__u8 *)RTA_DATA(attrs)); |
193 | else if (attrs->rta_type == MPTCP_ATTR_ERROR) |
194 | fprintf(stderr, ",error:%u" , *(__u8 *)RTA_DATA(attrs)); |
195 | else if (attrs->rta_type == MPTCP_ATTR_SERVER_SIDE) |
196 | fprintf(stderr, ",server_side:%u" , *(__u8 *)RTA_DATA(attrs)); |
197 | |
198 | attrs = RTA_NEXT(attrs, msg_len); |
199 | } |
200 | } |
201 | fprintf(stderr, "\n" ); |
202 | } while (1); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | /* do a netlink command and, if max > 0, fetch the reply ; nh's size >1024B */ |
208 | static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max) |
209 | { |
210 | struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; |
211 | socklen_t addr_len; |
212 | void *data = nh; |
213 | int rem, ret; |
214 | int err = 0; |
215 | |
216 | /* If no expected answer, ask for an ACK to look for errors if any */ |
217 | if (max == 0) { |
218 | nh->nlmsg_flags |= NLM_F_ACK; |
219 | max = 1024; |
220 | } |
221 | |
222 | nh->nlmsg_len = len; |
223 | ret = sendto(fd, data, len, 0, (void *)&nladdr, sizeof(nladdr)); |
224 | if (ret != len) |
225 | error(1, errno, "send netlink: %uB != %uB\n" , ret, len); |
226 | |
227 | addr_len = sizeof(nladdr); |
228 | rem = ret = recvfrom(fd, data, max, 0, (void *)&nladdr, &addr_len); |
229 | if (ret < 0) |
230 | error(1, errno, "recv netlink: %uB\n" , ret); |
231 | |
232 | /* Beware: the NLMSG_NEXT macro updates the 'rem' argument */ |
233 | for (; NLMSG_OK(nh, rem); nh = NLMSG_NEXT(nh, rem)) { |
234 | if (nh->nlmsg_type == NLMSG_DONE) |
235 | break; |
236 | |
237 | if (nh->nlmsg_type == NLMSG_ERROR && nl_error(nh)) |
238 | err = 1; |
239 | } |
240 | if (err) |
241 | error(1, 0, "bailing out due to netlink error[s]" ); |
242 | return ret; |
243 | } |
244 | |
245 | static int genl_parse_getfamily(struct nlmsghdr *nlh, int *pm_family, |
246 | int *events_mcast_grp) |
247 | { |
248 | struct genlmsghdr *ghdr = NLMSG_DATA(nlh); |
249 | int len = nlh->nlmsg_len; |
250 | struct rtattr *attrs; |
251 | struct rtattr *grps; |
252 | struct rtattr *grp; |
253 | int got_events_grp; |
254 | int got_family; |
255 | int grps_len; |
256 | int grp_len; |
257 | |
258 | if (nlh->nlmsg_type != GENL_ID_CTRL) |
259 | error(1, errno, "Not a controller message, len=%d type=0x%x\n" , |
260 | nlh->nlmsg_len, nlh->nlmsg_type); |
261 | |
262 | len -= NLMSG_LENGTH(GENL_HDRLEN); |
263 | |
264 | if (len < 0) |
265 | error(1, errno, "wrong controller message len %d\n" , len); |
266 | |
267 | if (ghdr->cmd != CTRL_CMD_NEWFAMILY) |
268 | error(1, errno, "Unknown controller command %d\n" , ghdr->cmd); |
269 | |
270 | attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); |
271 | got_family = 0; |
272 | got_events_grp = 0; |
273 | |
274 | while (RTA_OK(attrs, len)) { |
275 | if (attrs->rta_type == CTRL_ATTR_FAMILY_ID) { |
276 | *pm_family = *(__u16 *)RTA_DATA(attrs); |
277 | got_family = 1; |
278 | } else if (attrs->rta_type == CTRL_ATTR_MCAST_GROUPS) { |
279 | grps = RTA_DATA(attrs); |
280 | grps_len = RTA_PAYLOAD(attrs); |
281 | |
282 | while (RTA_OK(grps, grps_len)) { |
283 | grp = RTA_DATA(grps); |
284 | grp_len = RTA_PAYLOAD(grps); |
285 | got_events_grp = 0; |
286 | |
287 | while (RTA_OK(grp, grp_len)) { |
288 | if (grp->rta_type == CTRL_ATTR_MCAST_GRP_ID) |
289 | *events_mcast_grp = *(__u32 *)RTA_DATA(grp); |
290 | else if (grp->rta_type == CTRL_ATTR_MCAST_GRP_NAME && |
291 | !strcmp(RTA_DATA(grp), MPTCP_PM_EVENTS)) |
292 | got_events_grp = 1; |
293 | |
294 | grp = RTA_NEXT(grp, grp_len); |
295 | } |
296 | |
297 | if (got_events_grp) |
298 | break; |
299 | |
300 | grps = RTA_NEXT(grps, grps_len); |
301 | } |
302 | } |
303 | |
304 | if (got_family && got_events_grp) |
305 | return 0; |
306 | |
307 | attrs = RTA_NEXT(attrs, len); |
308 | } |
309 | |
310 | error(1, errno, "can't find CTRL_ATTR_FAMILY_ID attr" ); |
311 | return -1; |
312 | } |
313 | |
314 | static int resolve_mptcp_pm_netlink(int fd, int *pm_family, int *events_mcast_grp) |
315 | { |
316 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
317 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
318 | 1024]; |
319 | struct nlmsghdr *nh; |
320 | struct rtattr *rta; |
321 | int namelen; |
322 | int off = 0; |
323 | |
324 | memset(data, 0, sizeof(data)); |
325 | nh = (void *)data; |
326 | off = init_genl_req(data, GENL_ID_CTRL, cmd: CTRL_CMD_GETFAMILY, version: 0); |
327 | |
328 | rta = (void *)(data + off); |
329 | namelen = strlen(MPTCP_PM_NAME) + 1; |
330 | rta->rta_type = CTRL_ATTR_FAMILY_NAME; |
331 | rta->rta_len = RTA_LENGTH(namelen); |
332 | memcpy(RTA_DATA(rta), MPTCP_PM_NAME, namelen); |
333 | off += NLMSG_ALIGN(rta->rta_len); |
334 | |
335 | do_nl_req(fd, nh, len: off, max: sizeof(data)); |
336 | return genl_parse_getfamily(nlh: (void *)data, pm_family, events_mcast_grp); |
337 | } |
338 | |
339 | int dsf(int fd, int pm_family, int argc, char *argv[]) |
340 | { |
341 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
342 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
343 | 1024]; |
344 | struct rtattr *rta, *addr; |
345 | u_int16_t family, port; |
346 | struct nlmsghdr *nh; |
347 | u_int32_t token; |
348 | int addr_start; |
349 | int off = 0; |
350 | int arg; |
351 | |
352 | const char *params[5]; |
353 | |
354 | memset(params, 0, 5 * sizeof(const char *)); |
355 | |
356 | memset(data, 0, sizeof(data)); |
357 | nh = (void *)data; |
358 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_SUBFLOW_DESTROY, |
359 | MPTCP_PM_VER); |
360 | |
361 | if (argc < 12) |
362 | syntax(argv); |
363 | |
364 | /* Params recorded in this order: |
365 | * <local-ip>, <local-port>, <remote-ip>, <remote-port>, <token> |
366 | */ |
367 | for (arg = 2; arg < argc; arg++) { |
368 | if (!strcmp(argv[arg], "lip" )) { |
369 | if (++arg >= argc) |
370 | error(1, 0, " missing local IP" ); |
371 | |
372 | params[0] = argv[arg]; |
373 | } else if (!strcmp(argv[arg], "lport" )) { |
374 | if (++arg >= argc) |
375 | error(1, 0, " missing local port" ); |
376 | |
377 | params[1] = argv[arg]; |
378 | } else if (!strcmp(argv[arg], "rip" )) { |
379 | if (++arg >= argc) |
380 | error(1, 0, " missing remote IP" ); |
381 | |
382 | params[2] = argv[arg]; |
383 | } else if (!strcmp(argv[arg], "rport" )) { |
384 | if (++arg >= argc) |
385 | error(1, 0, " missing remote port" ); |
386 | |
387 | params[3] = argv[arg]; |
388 | } else if (!strcmp(argv[arg], "token" )) { |
389 | if (++arg >= argc) |
390 | error(1, 0, " missing token" ); |
391 | |
392 | params[4] = argv[arg]; |
393 | } else |
394 | error(1, 0, "unknown keyword %s" , argv[arg]); |
395 | } |
396 | |
397 | for (arg = 0; arg < 4; arg = arg + 2) { |
398 | /* addr header */ |
399 | addr_start = off; |
400 | addr = (void *)(data + off); |
401 | addr->rta_type = NLA_F_NESTED | |
402 | ((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE); |
403 | addr->rta_len = RTA_LENGTH(0); |
404 | off += NLMSG_ALIGN(addr->rta_len); |
405 | |
406 | /* addr data */ |
407 | rta = (void *)(data + off); |
408 | if (inet_pton(AF_INET, params[arg], RTA_DATA(rta))) { |
409 | family = AF_INET; |
410 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
411 | rta->rta_len = RTA_LENGTH(4); |
412 | } else if (inet_pton(AF_INET6, params[arg], RTA_DATA(rta))) { |
413 | family = AF_INET6; |
414 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
415 | rta->rta_len = RTA_LENGTH(16); |
416 | } else |
417 | error(1, errno, "can't parse ip %s" , params[arg]); |
418 | off += NLMSG_ALIGN(rta->rta_len); |
419 | |
420 | /* family */ |
421 | rta = (void *)(data + off); |
422 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
423 | rta->rta_len = RTA_LENGTH(2); |
424 | memcpy(RTA_DATA(rta), &family, 2); |
425 | off += NLMSG_ALIGN(rta->rta_len); |
426 | |
427 | /* port */ |
428 | port = atoi(params[arg + 1]); |
429 | rta = (void *)(data + off); |
430 | rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
431 | rta->rta_len = RTA_LENGTH(2); |
432 | memcpy(RTA_DATA(rta), &port, 2); |
433 | off += NLMSG_ALIGN(rta->rta_len); |
434 | |
435 | addr->rta_len = off - addr_start; |
436 | } |
437 | |
438 | /* token */ |
439 | token = strtoul(params[4], NULL, 10); |
440 | rta = (void *)(data + off); |
441 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
442 | rta->rta_len = RTA_LENGTH(4); |
443 | memcpy(RTA_DATA(rta), &token, 4); |
444 | off += NLMSG_ALIGN(rta->rta_len); |
445 | |
446 | do_nl_req(fd, nh, len: off, max: 0); |
447 | |
448 | return 0; |
449 | } |
450 | |
451 | int csf(int fd, int pm_family, int argc, char *argv[]) |
452 | { |
453 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
454 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
455 | 1024]; |
456 | u_int32_t flags = MPTCP_PM_ADDR_FLAG_SUBFLOW; |
457 | const char *params[5]; |
458 | struct nlmsghdr *nh; |
459 | struct rtattr *addr; |
460 | struct rtattr *rta; |
461 | u_int16_t family; |
462 | u_int32_t token; |
463 | u_int16_t port; |
464 | int addr_start; |
465 | u_int8_t id; |
466 | int off = 0; |
467 | int arg; |
468 | |
469 | memset(params, 0, 5 * sizeof(const char *)); |
470 | |
471 | memset(data, 0, sizeof(data)); |
472 | nh = (void *)data; |
473 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_SUBFLOW_CREATE, |
474 | MPTCP_PM_VER); |
475 | |
476 | if (argc < 12) |
477 | syntax(argv); |
478 | |
479 | /* Params recorded in this order: |
480 | * <local-ip>, <local-id>, <remote-ip>, <remote-port>, <token> |
481 | */ |
482 | for (arg = 2; arg < argc; arg++) { |
483 | if (!strcmp(argv[arg], "lip" )) { |
484 | if (++arg >= argc) |
485 | error(1, 0, " missing local IP" ); |
486 | |
487 | params[0] = argv[arg]; |
488 | } else if (!strcmp(argv[arg], "lid" )) { |
489 | if (++arg >= argc) |
490 | error(1, 0, " missing local id" ); |
491 | |
492 | params[1] = argv[arg]; |
493 | } else if (!strcmp(argv[arg], "rip" )) { |
494 | if (++arg >= argc) |
495 | error(1, 0, " missing remote ip" ); |
496 | |
497 | params[2] = argv[arg]; |
498 | } else if (!strcmp(argv[arg], "rport" )) { |
499 | if (++arg >= argc) |
500 | error(1, 0, " missing remote port" ); |
501 | |
502 | params[3] = argv[arg]; |
503 | } else if (!strcmp(argv[arg], "token" )) { |
504 | if (++arg >= argc) |
505 | error(1, 0, " missing token" ); |
506 | |
507 | params[4] = argv[arg]; |
508 | } else |
509 | error(1, 0, "unknown param %s" , argv[arg]); |
510 | } |
511 | |
512 | for (arg = 0; arg < 4; arg = arg + 2) { |
513 | /* addr header */ |
514 | addr_start = off; |
515 | addr = (void *)(data + off); |
516 | addr->rta_type = NLA_F_NESTED | |
517 | ((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE); |
518 | addr->rta_len = RTA_LENGTH(0); |
519 | off += NLMSG_ALIGN(addr->rta_len); |
520 | |
521 | /* addr data */ |
522 | rta = (void *)(data + off); |
523 | if (inet_pton(AF_INET, params[arg], RTA_DATA(rta))) { |
524 | family = AF_INET; |
525 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
526 | rta->rta_len = RTA_LENGTH(4); |
527 | } else if (inet_pton(AF_INET6, params[arg], RTA_DATA(rta))) { |
528 | family = AF_INET6; |
529 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
530 | rta->rta_len = RTA_LENGTH(16); |
531 | } else |
532 | error(1, errno, "can't parse ip %s" , params[arg]); |
533 | off += NLMSG_ALIGN(rta->rta_len); |
534 | |
535 | /* family */ |
536 | rta = (void *)(data + off); |
537 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
538 | rta->rta_len = RTA_LENGTH(2); |
539 | memcpy(RTA_DATA(rta), &family, 2); |
540 | off += NLMSG_ALIGN(rta->rta_len); |
541 | |
542 | if (arg == 2) { |
543 | /* port */ |
544 | port = atoi(params[arg + 1]); |
545 | rta = (void *)(data + off); |
546 | rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
547 | rta->rta_len = RTA_LENGTH(2); |
548 | memcpy(RTA_DATA(rta), &port, 2); |
549 | off += NLMSG_ALIGN(rta->rta_len); |
550 | } |
551 | |
552 | if (arg == 0) { |
553 | /* id */ |
554 | id = atoi(params[arg + 1]); |
555 | rta = (void *)(data + off); |
556 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
557 | rta->rta_len = RTA_LENGTH(1); |
558 | memcpy(RTA_DATA(rta), &id, 1); |
559 | off += NLMSG_ALIGN(rta->rta_len); |
560 | } |
561 | |
562 | /* addr flags */ |
563 | rta = (void *)(data + off); |
564 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
565 | rta->rta_len = RTA_LENGTH(4); |
566 | memcpy(RTA_DATA(rta), &flags, 4); |
567 | off += NLMSG_ALIGN(rta->rta_len); |
568 | |
569 | addr->rta_len = off - addr_start; |
570 | } |
571 | |
572 | /* token */ |
573 | token = strtoul(params[4], NULL, 10); |
574 | rta = (void *)(data + off); |
575 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
576 | rta->rta_len = RTA_LENGTH(4); |
577 | memcpy(RTA_DATA(rta), &token, 4); |
578 | off += NLMSG_ALIGN(rta->rta_len); |
579 | |
580 | do_nl_req(fd, nh, len: off, max: 0); |
581 | |
582 | return 0; |
583 | } |
584 | |
585 | int remove_addr(int fd, int pm_family, int argc, char *argv[]) |
586 | { |
587 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
588 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
589 | 1024]; |
590 | struct nlmsghdr *nh; |
591 | struct rtattr *rta; |
592 | u_int32_t token; |
593 | u_int8_t id; |
594 | int off = 0; |
595 | int arg; |
596 | |
597 | memset(data, 0, sizeof(data)); |
598 | nh = (void *)data; |
599 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_REMOVE, |
600 | MPTCP_PM_VER); |
601 | |
602 | if (argc < 6) |
603 | syntax(argv); |
604 | |
605 | for (arg = 2; arg < argc; arg++) { |
606 | if (!strcmp(argv[arg], "id" )) { |
607 | if (++arg >= argc) |
608 | error(1, 0, " missing id value" ); |
609 | |
610 | id = atoi(argv[arg]); |
611 | rta = (void *)(data + off); |
612 | rta->rta_type = MPTCP_PM_ATTR_LOC_ID; |
613 | rta->rta_len = RTA_LENGTH(1); |
614 | memcpy(RTA_DATA(rta), &id, 1); |
615 | off += NLMSG_ALIGN(rta->rta_len); |
616 | } else if (!strcmp(argv[arg], "token" )) { |
617 | if (++arg >= argc) |
618 | error(1, 0, " missing token value" ); |
619 | |
620 | token = strtoul(argv[arg], NULL, 10); |
621 | rta = (void *)(data + off); |
622 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
623 | rta->rta_len = RTA_LENGTH(4); |
624 | memcpy(RTA_DATA(rta), &token, 4); |
625 | off += NLMSG_ALIGN(rta->rta_len); |
626 | } else |
627 | error(1, 0, "unknown keyword %s" , argv[arg]); |
628 | } |
629 | |
630 | do_nl_req(fd, nh, len: off, max: 0); |
631 | return 0; |
632 | } |
633 | |
634 | int announce_addr(int fd, int pm_family, int argc, char *argv[]) |
635 | { |
636 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
637 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
638 | 1024]; |
639 | u_int32_t flags = MPTCP_PM_ADDR_FLAG_SIGNAL; |
640 | u_int32_t token = UINT_MAX; |
641 | struct rtattr *rta, *addr; |
642 | u_int32_t id = UINT_MAX; |
643 | struct nlmsghdr *nh; |
644 | u_int16_t family; |
645 | int addr_start; |
646 | int off = 0; |
647 | int arg; |
648 | |
649 | memset(data, 0, sizeof(data)); |
650 | nh = (void *)data; |
651 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_ANNOUNCE, |
652 | MPTCP_PM_VER); |
653 | |
654 | if (argc < 7) |
655 | syntax(argv); |
656 | |
657 | /* local-ip header */ |
658 | addr_start = off; |
659 | addr = (void *)(data + off); |
660 | addr->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
661 | addr->rta_len = RTA_LENGTH(0); |
662 | off += NLMSG_ALIGN(addr->rta_len); |
663 | |
664 | /* local-ip data */ |
665 | /* record addr type */ |
666 | rta = (void *)(data + off); |
667 | if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) { |
668 | family = AF_INET; |
669 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
670 | rta->rta_len = RTA_LENGTH(4); |
671 | } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) { |
672 | family = AF_INET6; |
673 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
674 | rta->rta_len = RTA_LENGTH(16); |
675 | } else |
676 | error(1, errno, "can't parse ip %s" , argv[2]); |
677 | off += NLMSG_ALIGN(rta->rta_len); |
678 | |
679 | /* addr family */ |
680 | rta = (void *)(data + off); |
681 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
682 | rta->rta_len = RTA_LENGTH(2); |
683 | memcpy(RTA_DATA(rta), &family, 2); |
684 | off += NLMSG_ALIGN(rta->rta_len); |
685 | |
686 | for (arg = 3; arg < argc; arg++) { |
687 | if (!strcmp(argv[arg], "id" )) { |
688 | /* local-id */ |
689 | if (++arg >= argc) |
690 | error(1, 0, " missing id value" ); |
691 | |
692 | id = atoi(argv[arg]); |
693 | rta = (void *)(data + off); |
694 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
695 | rta->rta_len = RTA_LENGTH(1); |
696 | memcpy(RTA_DATA(rta), &id, 1); |
697 | off += NLMSG_ALIGN(rta->rta_len); |
698 | } else if (!strcmp(argv[arg], "dev" )) { |
699 | /* for the if_index */ |
700 | int32_t ifindex; |
701 | |
702 | if (++arg >= argc) |
703 | error(1, 0, " missing dev name" ); |
704 | |
705 | ifindex = if_nametoindex(argv[arg]); |
706 | if (!ifindex) |
707 | error(1, errno, "unknown device %s" , argv[arg]); |
708 | |
709 | rta = (void *)(data + off); |
710 | rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX; |
711 | rta->rta_len = RTA_LENGTH(4); |
712 | memcpy(RTA_DATA(rta), &ifindex, 4); |
713 | off += NLMSG_ALIGN(rta->rta_len); |
714 | } else if (!strcmp(argv[arg], "port" )) { |
715 | /* local-port (optional) */ |
716 | u_int16_t port; |
717 | |
718 | if (++arg >= argc) |
719 | error(1, 0, " missing port value" ); |
720 | |
721 | port = atoi(argv[arg]); |
722 | rta = (void *)(data + off); |
723 | rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
724 | rta->rta_len = RTA_LENGTH(2); |
725 | memcpy(RTA_DATA(rta), &port, 2); |
726 | off += NLMSG_ALIGN(rta->rta_len); |
727 | } else if (!strcmp(argv[arg], "token" )) { |
728 | /* MPTCP connection token */ |
729 | if (++arg >= argc) |
730 | error(1, 0, " missing token value" ); |
731 | |
732 | token = strtoul(argv[arg], NULL, 10); |
733 | } else |
734 | error(1, 0, "unknown keyword %s" , argv[arg]); |
735 | } |
736 | |
737 | /* addr flags */ |
738 | rta = (void *)(data + off); |
739 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
740 | rta->rta_len = RTA_LENGTH(4); |
741 | memcpy(RTA_DATA(rta), &flags, 4); |
742 | off += NLMSG_ALIGN(rta->rta_len); |
743 | |
744 | addr->rta_len = off - addr_start; |
745 | |
746 | if (id == UINT_MAX || token == UINT_MAX) |
747 | error(1, 0, " missing mandatory inputs" ); |
748 | |
749 | /* token */ |
750 | rta = (void *)(data + off); |
751 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
752 | rta->rta_len = RTA_LENGTH(4); |
753 | memcpy(RTA_DATA(rta), &token, 4); |
754 | off += NLMSG_ALIGN(rta->rta_len); |
755 | |
756 | do_nl_req(fd, nh, len: off, max: 0); |
757 | |
758 | return 0; |
759 | } |
760 | |
761 | int add_addr(int fd, int pm_family, int argc, char *argv[]) |
762 | { |
763 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
764 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
765 | 1024]; |
766 | struct rtattr *rta, *nest; |
767 | struct nlmsghdr *nh; |
768 | u_int32_t flags = 0; |
769 | u_int16_t family; |
770 | int nest_start; |
771 | u_int8_t id; |
772 | int off = 0; |
773 | int arg; |
774 | |
775 | memset(data, 0, sizeof(data)); |
776 | nh = (void *)data; |
777 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_ADD_ADDR, |
778 | MPTCP_PM_VER); |
779 | |
780 | if (argc < 3) |
781 | syntax(argv); |
782 | |
783 | nest_start = off; |
784 | nest = (void *)(data + off); |
785 | nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
786 | nest->rta_len = RTA_LENGTH(0); |
787 | off += NLMSG_ALIGN(nest->rta_len); |
788 | |
789 | /* addr data */ |
790 | rta = (void *)(data + off); |
791 | if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) { |
792 | family = AF_INET; |
793 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
794 | rta->rta_len = RTA_LENGTH(4); |
795 | } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) { |
796 | family = AF_INET6; |
797 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
798 | rta->rta_len = RTA_LENGTH(16); |
799 | } else |
800 | error(1, errno, "can't parse ip %s" , argv[2]); |
801 | off += NLMSG_ALIGN(rta->rta_len); |
802 | |
803 | /* family */ |
804 | rta = (void *)(data + off); |
805 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
806 | rta->rta_len = RTA_LENGTH(2); |
807 | memcpy(RTA_DATA(rta), &family, 2); |
808 | off += NLMSG_ALIGN(rta->rta_len); |
809 | |
810 | for (arg = 3; arg < argc; arg++) { |
811 | if (!strcmp(argv[arg], "flags" )) { |
812 | char *tok, *str; |
813 | |
814 | /* flags */ |
815 | if (++arg >= argc) |
816 | error(1, 0, " missing flags value" ); |
817 | |
818 | /* do not support flag list yet */ |
819 | for (str = argv[arg]; (tok = strtok(str, "," )); |
820 | str = NULL) { |
821 | if (!strcmp(tok, "subflow" )) |
822 | flags |= MPTCP_PM_ADDR_FLAG_SUBFLOW; |
823 | else if (!strcmp(tok, "signal" )) |
824 | flags |= MPTCP_PM_ADDR_FLAG_SIGNAL; |
825 | else if (!strcmp(tok, "backup" )) |
826 | flags |= MPTCP_PM_ADDR_FLAG_BACKUP; |
827 | else if (!strcmp(tok, "fullmesh" )) |
828 | flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; |
829 | else |
830 | error(1, errno, |
831 | "unknown flag %s" , argv[arg]); |
832 | } |
833 | |
834 | if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL && |
835 | flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { |
836 | error(1, errno, "error flag fullmesh" ); |
837 | } |
838 | |
839 | rta = (void *)(data + off); |
840 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
841 | rta->rta_len = RTA_LENGTH(4); |
842 | memcpy(RTA_DATA(rta), &flags, 4); |
843 | off += NLMSG_ALIGN(rta->rta_len); |
844 | } else if (!strcmp(argv[arg], "id" )) { |
845 | if (++arg >= argc) |
846 | error(1, 0, " missing id value" ); |
847 | |
848 | id = atoi(argv[arg]); |
849 | rta = (void *)(data + off); |
850 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
851 | rta->rta_len = RTA_LENGTH(1); |
852 | memcpy(RTA_DATA(rta), &id, 1); |
853 | off += NLMSG_ALIGN(rta->rta_len); |
854 | } else if (!strcmp(argv[arg], "dev" )) { |
855 | int32_t ifindex; |
856 | |
857 | if (++arg >= argc) |
858 | error(1, 0, " missing dev name" ); |
859 | |
860 | ifindex = if_nametoindex(argv[arg]); |
861 | if (!ifindex) |
862 | error(1, errno, "unknown device %s" , argv[arg]); |
863 | |
864 | rta = (void *)(data + off); |
865 | rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX; |
866 | rta->rta_len = RTA_LENGTH(4); |
867 | memcpy(RTA_DATA(rta), &ifindex, 4); |
868 | off += NLMSG_ALIGN(rta->rta_len); |
869 | } else if (!strcmp(argv[arg], "port" )) { |
870 | u_int16_t port; |
871 | |
872 | if (++arg >= argc) |
873 | error(1, 0, " missing port value" ); |
874 | if (!(flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) |
875 | error(1, 0, " flags must be signal when using port" ); |
876 | |
877 | port = atoi(argv[arg]); |
878 | rta = (void *)(data + off); |
879 | rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
880 | rta->rta_len = RTA_LENGTH(2); |
881 | memcpy(RTA_DATA(rta), &port, 2); |
882 | off += NLMSG_ALIGN(rta->rta_len); |
883 | } else |
884 | error(1, 0, "unknown keyword %s" , argv[arg]); |
885 | } |
886 | nest->rta_len = off - nest_start; |
887 | |
888 | do_nl_req(fd, nh, len: off, max: 0); |
889 | return 0; |
890 | } |
891 | |
892 | int del_addr(int fd, int pm_family, int argc, char *argv[]) |
893 | { |
894 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
895 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
896 | 1024]; |
897 | struct rtattr *rta, *nest; |
898 | struct nlmsghdr *nh; |
899 | u_int16_t family; |
900 | int nest_start; |
901 | u_int8_t id; |
902 | int off = 0; |
903 | |
904 | memset(data, 0, sizeof(data)); |
905 | nh = (void *)data; |
906 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_DEL_ADDR, |
907 | MPTCP_PM_VER); |
908 | |
909 | /* the only argument is the address id (nonzero) */ |
910 | if (argc != 3 && argc != 4) |
911 | syntax(argv); |
912 | |
913 | id = atoi(argv[2]); |
914 | /* zero id with the IP address */ |
915 | if (!id && argc != 4) |
916 | syntax(argv); |
917 | |
918 | nest_start = off; |
919 | nest = (void *)(data + off); |
920 | nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
921 | nest->rta_len = RTA_LENGTH(0); |
922 | off += NLMSG_ALIGN(nest->rta_len); |
923 | |
924 | /* build a dummy addr with only the ID set */ |
925 | rta = (void *)(data + off); |
926 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
927 | rta->rta_len = RTA_LENGTH(1); |
928 | memcpy(RTA_DATA(rta), &id, 1); |
929 | off += NLMSG_ALIGN(rta->rta_len); |
930 | |
931 | if (!id) { |
932 | /* addr data */ |
933 | rta = (void *)(data + off); |
934 | if (inet_pton(AF_INET, argv[3], RTA_DATA(rta))) { |
935 | family = AF_INET; |
936 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
937 | rta->rta_len = RTA_LENGTH(4); |
938 | } else if (inet_pton(AF_INET6, argv[3], RTA_DATA(rta))) { |
939 | family = AF_INET6; |
940 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
941 | rta->rta_len = RTA_LENGTH(16); |
942 | } else { |
943 | error(1, errno, "can't parse ip %s" , argv[3]); |
944 | } |
945 | off += NLMSG_ALIGN(rta->rta_len); |
946 | |
947 | /* family */ |
948 | rta = (void *)(data + off); |
949 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
950 | rta->rta_len = RTA_LENGTH(2); |
951 | memcpy(RTA_DATA(rta), &family, 2); |
952 | off += NLMSG_ALIGN(rta->rta_len); |
953 | } |
954 | nest->rta_len = off - nest_start; |
955 | |
956 | do_nl_req(fd, nh, len: off, max: 0); |
957 | return 0; |
958 | } |
959 | |
960 | static void print_addr(struct rtattr *attrs, int len) |
961 | { |
962 | uint16_t family = 0; |
963 | uint16_t port = 0; |
964 | char str[1024]; |
965 | uint32_t flags; |
966 | uint8_t id; |
967 | |
968 | while (RTA_OK(attrs, len)) { |
969 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FAMILY) |
970 | memcpy(&family, RTA_DATA(attrs), 2); |
971 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_PORT) |
972 | memcpy(&port, RTA_DATA(attrs), 2); |
973 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR4) { |
974 | if (family != AF_INET) |
975 | error(1, errno, "wrong IP (v4) for family %d" , |
976 | family); |
977 | inet_ntop(AF_INET, RTA_DATA(attrs), str, sizeof(str)); |
978 | printf("%s" , str); |
979 | if (port) |
980 | printf(" %d" , port); |
981 | } |
982 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR6) { |
983 | if (family != AF_INET6) |
984 | error(1, errno, "wrong IP (v6) for family %d" , |
985 | family); |
986 | inet_ntop(AF_INET6, RTA_DATA(attrs), str, sizeof(str)); |
987 | printf("%s" , str); |
988 | if (port) |
989 | printf(" %d" , port); |
990 | } |
991 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ID) { |
992 | memcpy(&id, RTA_DATA(attrs), 1); |
993 | printf("id %d " , id); |
994 | } |
995 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FLAGS) { |
996 | memcpy(&flags, RTA_DATA(attrs), 4); |
997 | |
998 | printf("flags " ); |
999 | if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL) { |
1000 | printf("signal" ); |
1001 | flags &= ~MPTCP_PM_ADDR_FLAG_SIGNAL; |
1002 | if (flags) |
1003 | printf("," ); |
1004 | } |
1005 | |
1006 | if (flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) { |
1007 | printf("subflow" ); |
1008 | flags &= ~MPTCP_PM_ADDR_FLAG_SUBFLOW; |
1009 | if (flags) |
1010 | printf("," ); |
1011 | } |
1012 | |
1013 | if (flags & MPTCP_PM_ADDR_FLAG_BACKUP) { |
1014 | printf("backup" ); |
1015 | flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP; |
1016 | if (flags) |
1017 | printf("," ); |
1018 | } |
1019 | |
1020 | if (flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { |
1021 | printf("fullmesh" ); |
1022 | flags &= ~MPTCP_PM_ADDR_FLAG_FULLMESH; |
1023 | if (flags) |
1024 | printf("," ); |
1025 | } |
1026 | |
1027 | if (flags & MPTCP_PM_ADDR_FLAG_IMPLICIT) { |
1028 | printf("implicit" ); |
1029 | flags &= ~MPTCP_PM_ADDR_FLAG_IMPLICIT; |
1030 | if (flags) |
1031 | printf("," ); |
1032 | } |
1033 | |
1034 | /* bump unknown flags, if any */ |
1035 | if (flags) |
1036 | printf("0x%x" , flags); |
1037 | printf(" " ); |
1038 | } |
1039 | if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_IF_IDX) { |
1040 | char name[IF_NAMESIZE], *ret; |
1041 | int32_t ifindex; |
1042 | |
1043 | memcpy(&ifindex, RTA_DATA(attrs), 4); |
1044 | ret = if_indextoname(ifindex, name); |
1045 | if (ret) |
1046 | printf("dev %s " , ret); |
1047 | else |
1048 | printf("dev unknown/%d" , ifindex); |
1049 | } |
1050 | |
1051 | attrs = RTA_NEXT(attrs, len); |
1052 | } |
1053 | printf("\n" ); |
1054 | } |
1055 | |
1056 | static void print_addrs(struct nlmsghdr *nh, int pm_family, int total_len) |
1057 | { |
1058 | struct rtattr *attrs; |
1059 | |
1060 | for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) { |
1061 | int len = nh->nlmsg_len; |
1062 | |
1063 | if (nh->nlmsg_type == NLMSG_DONE) |
1064 | break; |
1065 | if (nh->nlmsg_type == NLMSG_ERROR) |
1066 | nl_error(nh); |
1067 | if (nh->nlmsg_type != pm_family) |
1068 | continue; |
1069 | |
1070 | len -= NLMSG_LENGTH(GENL_HDRLEN); |
1071 | attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) + |
1072 | GENL_HDRLEN); |
1073 | while (RTA_OK(attrs, len)) { |
1074 | if (attrs->rta_type == |
1075 | (MPTCP_PM_ATTR_ADDR | NLA_F_NESTED)) |
1076 | print_addr(attrs: (void *)RTA_DATA(attrs), |
1077 | len: attrs->rta_len); |
1078 | attrs = RTA_NEXT(attrs, len); |
1079 | } |
1080 | } |
1081 | } |
1082 | |
1083 | int get_addr(int fd, int pm_family, int argc, char *argv[]) |
1084 | { |
1085 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
1086 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
1087 | 1024]; |
1088 | struct rtattr *rta, *nest; |
1089 | struct nlmsghdr *nh; |
1090 | u_int32_t token = 0; |
1091 | int nest_start; |
1092 | u_int8_t id; |
1093 | int off = 0; |
1094 | |
1095 | memset(data, 0, sizeof(data)); |
1096 | nh = (void *)data; |
1097 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_GET_ADDR, |
1098 | MPTCP_PM_VER); |
1099 | |
1100 | /* the only argument is the address id */ |
1101 | if (argc != 3 && argc != 5) |
1102 | syntax(argv); |
1103 | |
1104 | id = atoi(argv[2]); |
1105 | if (argc == 5 && !strcmp(argv[3], "token" )) |
1106 | token = strtoul(argv[4], NULL, 10); |
1107 | |
1108 | nest_start = off; |
1109 | nest = (void *)(data + off); |
1110 | nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
1111 | nest->rta_len = RTA_LENGTH(0); |
1112 | off += NLMSG_ALIGN(nest->rta_len); |
1113 | |
1114 | /* build a dummy addr with only the ID set */ |
1115 | rta = (void *)(data + off); |
1116 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
1117 | rta->rta_len = RTA_LENGTH(1); |
1118 | memcpy(RTA_DATA(rta), &id, 1); |
1119 | off += NLMSG_ALIGN(rta->rta_len); |
1120 | nest->rta_len = off - nest_start; |
1121 | |
1122 | /* token */ |
1123 | if (token) { |
1124 | rta = (void *)(data + off); |
1125 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
1126 | rta->rta_len = RTA_LENGTH(4); |
1127 | memcpy(RTA_DATA(rta), &token, 4); |
1128 | off += NLMSG_ALIGN(rta->rta_len); |
1129 | } |
1130 | |
1131 | print_addrs(nh, pm_family, total_len: do_nl_req(fd, nh, len: off, max: sizeof(data))); |
1132 | return 0; |
1133 | } |
1134 | |
1135 | int dump_addrs(int fd, int pm_family, int argc, char *argv[]) |
1136 | { |
1137 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
1138 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
1139 | 1024]; |
1140 | pid_t pid = getpid(); |
1141 | struct nlmsghdr *nh; |
1142 | u_int32_t token = 0; |
1143 | struct rtattr *rta; |
1144 | int off = 0; |
1145 | |
1146 | if (argc != 2 && argc != 4) |
1147 | syntax(argv); |
1148 | |
1149 | if (argc == 4 && !strcmp(argv[2], "token" )) |
1150 | token = strtoul(argv[3], NULL, 10); |
1151 | |
1152 | memset(data, 0, sizeof(data)); |
1153 | nh = (void *)data; |
1154 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_GET_ADDR, |
1155 | MPTCP_PM_VER); |
1156 | nh->nlmsg_flags |= NLM_F_DUMP; |
1157 | nh->nlmsg_seq = 1; |
1158 | nh->nlmsg_pid = pid; |
1159 | nh->nlmsg_len = off; |
1160 | |
1161 | /* token */ |
1162 | if (token) { |
1163 | rta = (void *)(data + off); |
1164 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
1165 | rta->rta_len = RTA_LENGTH(4); |
1166 | memcpy(RTA_DATA(rta), &token, 4); |
1167 | off += NLMSG_ALIGN(rta->rta_len); |
1168 | } |
1169 | |
1170 | print_addrs(nh, pm_family, total_len: do_nl_req(fd, nh, len: off, max: sizeof(data))); |
1171 | return 0; |
1172 | } |
1173 | |
1174 | int flush_addrs(int fd, int pm_family, int argc, char *argv[]) |
1175 | { |
1176 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
1177 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
1178 | 1024]; |
1179 | struct nlmsghdr *nh; |
1180 | int off = 0; |
1181 | |
1182 | memset(data, 0, sizeof(data)); |
1183 | nh = (void *)data; |
1184 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_FLUSH_ADDRS, |
1185 | MPTCP_PM_VER); |
1186 | |
1187 | do_nl_req(fd, nh, len: off, max: 0); |
1188 | return 0; |
1189 | } |
1190 | |
1191 | static void print_limits(struct nlmsghdr *nh, int pm_family, int total_len) |
1192 | { |
1193 | struct rtattr *attrs; |
1194 | uint32_t max; |
1195 | |
1196 | for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) { |
1197 | int len = nh->nlmsg_len; |
1198 | |
1199 | if (nh->nlmsg_type == NLMSG_DONE) |
1200 | break; |
1201 | if (nh->nlmsg_type == NLMSG_ERROR) |
1202 | nl_error(nh); |
1203 | if (nh->nlmsg_type != pm_family) |
1204 | continue; |
1205 | |
1206 | len -= NLMSG_LENGTH(GENL_HDRLEN); |
1207 | attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) + |
1208 | GENL_HDRLEN); |
1209 | while (RTA_OK(attrs, len)) { |
1210 | int type = attrs->rta_type; |
1211 | |
1212 | if (type != MPTCP_PM_ATTR_RCV_ADD_ADDRS && |
1213 | type != MPTCP_PM_ATTR_SUBFLOWS) |
1214 | goto next; |
1215 | |
1216 | memcpy(&max, RTA_DATA(attrs), 4); |
1217 | printf("%s %u\n" , type == MPTCP_PM_ATTR_SUBFLOWS ? |
1218 | "subflows" : "accept" , max); |
1219 | |
1220 | next: |
1221 | attrs = RTA_NEXT(attrs, len); |
1222 | } |
1223 | } |
1224 | } |
1225 | |
1226 | int get_set_limits(int fd, int pm_family, int argc, char *argv[]) |
1227 | { |
1228 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
1229 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
1230 | 1024]; |
1231 | uint32_t rcv_addr = 0, subflows = 0; |
1232 | int cmd, len = sizeof(data); |
1233 | struct nlmsghdr *nh; |
1234 | int off = 0; |
1235 | |
1236 | /* limit */ |
1237 | if (argc == 4) { |
1238 | rcv_addr = atoi(argv[2]); |
1239 | subflows = atoi(argv[3]); |
1240 | cmd = MPTCP_PM_CMD_SET_LIMITS; |
1241 | } else { |
1242 | cmd = MPTCP_PM_CMD_GET_LIMITS; |
1243 | } |
1244 | |
1245 | memset(data, 0, sizeof(data)); |
1246 | nh = (void *)data; |
1247 | off = init_genl_req(data, family: pm_family, cmd, MPTCP_PM_VER); |
1248 | |
1249 | /* limit */ |
1250 | if (cmd == MPTCP_PM_CMD_SET_LIMITS) { |
1251 | struct rtattr *rta = (void *)(data + off); |
1252 | |
1253 | rta->rta_type = MPTCP_PM_ATTR_RCV_ADD_ADDRS; |
1254 | rta->rta_len = RTA_LENGTH(4); |
1255 | memcpy(RTA_DATA(rta), &rcv_addr, 4); |
1256 | off += NLMSG_ALIGN(rta->rta_len); |
1257 | |
1258 | rta = (void *)(data + off); |
1259 | rta->rta_type = MPTCP_PM_ATTR_SUBFLOWS; |
1260 | rta->rta_len = RTA_LENGTH(4); |
1261 | memcpy(RTA_DATA(rta), &subflows, 4); |
1262 | off += NLMSG_ALIGN(rta->rta_len); |
1263 | |
1264 | /* do not expect a reply */ |
1265 | len = 0; |
1266 | } |
1267 | |
1268 | len = do_nl_req(fd, nh, len: off, max: len); |
1269 | if (cmd == MPTCP_PM_CMD_GET_LIMITS) |
1270 | print_limits(nh, pm_family, total_len: len); |
1271 | return 0; |
1272 | } |
1273 | |
1274 | int add_listener(int argc, char *argv[]) |
1275 | { |
1276 | struct sockaddr_storage addr; |
1277 | struct sockaddr_in6 *a6; |
1278 | struct sockaddr_in *a4; |
1279 | u_int16_t family; |
1280 | int enable = 1; |
1281 | int sock; |
1282 | int err; |
1283 | |
1284 | if (argc < 4) |
1285 | syntax(argv); |
1286 | |
1287 | memset(&addr, 0, sizeof(struct sockaddr_storage)); |
1288 | a4 = (struct sockaddr_in *)&addr; |
1289 | a6 = (struct sockaddr_in6 *)&addr; |
1290 | |
1291 | if (inet_pton(AF_INET, argv[2], &a4->sin_addr)) { |
1292 | family = AF_INET; |
1293 | a4->sin_family = family; |
1294 | a4->sin_port = htons(atoi(argv[3])); |
1295 | } else if (inet_pton(AF_INET6, argv[2], &a6->sin6_addr)) { |
1296 | family = AF_INET6; |
1297 | a6->sin6_family = family; |
1298 | a6->sin6_port = htons(atoi(argv[3])); |
1299 | } else |
1300 | error(1, errno, "can't parse ip %s" , argv[2]); |
1301 | |
1302 | sock = socket(family, SOCK_STREAM, IPPROTO_MPTCP); |
1303 | if (sock < 0) |
1304 | error(1, errno, "can't create listener sock\n" ); |
1305 | |
1306 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable))) { |
1307 | close(sock); |
1308 | error(1, errno, "can't set SO_REUSEADDR on listener sock\n" ); |
1309 | } |
1310 | |
1311 | err = bind(sock, (struct sockaddr *)&addr, |
1312 | ((family == AF_INET) ? sizeof(struct sockaddr_in) : |
1313 | sizeof(struct sockaddr_in6))); |
1314 | |
1315 | if (err == 0 && listen(sock, 30) == 0) |
1316 | pause(); |
1317 | |
1318 | close(sock); |
1319 | return 0; |
1320 | } |
1321 | |
1322 | int set_flags(int fd, int pm_family, int argc, char *argv[]) |
1323 | { |
1324 | char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
1325 | NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
1326 | 1024]; |
1327 | struct rtattr *rta, *nest; |
1328 | struct nlmsghdr *nh; |
1329 | u_int32_t flags = 0; |
1330 | u_int32_t token = 0; |
1331 | u_int16_t rport = 0; |
1332 | u_int16_t family; |
1333 | void *rip = NULL; |
1334 | int nest_start; |
1335 | int use_id = 0; |
1336 | u_int8_t id; |
1337 | int off = 0; |
1338 | int arg = 2; |
1339 | |
1340 | memset(data, 0, sizeof(data)); |
1341 | nh = (void *)data; |
1342 | off = init_genl_req(data, family: pm_family, cmd: MPTCP_PM_CMD_SET_FLAGS, |
1343 | MPTCP_PM_VER); |
1344 | |
1345 | if (argc < 3) |
1346 | syntax(argv); |
1347 | |
1348 | nest_start = off; |
1349 | nest = (void *)(data + off); |
1350 | nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
1351 | nest->rta_len = RTA_LENGTH(0); |
1352 | off += NLMSG_ALIGN(nest->rta_len); |
1353 | |
1354 | if (!strcmp(argv[arg], "id" )) { |
1355 | if (++arg >= argc) |
1356 | error(1, 0, " missing id value" ); |
1357 | |
1358 | use_id = 1; |
1359 | id = atoi(argv[arg]); |
1360 | rta = (void *)(data + off); |
1361 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
1362 | rta->rta_len = RTA_LENGTH(1); |
1363 | memcpy(RTA_DATA(rta), &id, 1); |
1364 | off += NLMSG_ALIGN(rta->rta_len); |
1365 | } else { |
1366 | /* addr data */ |
1367 | rta = (void *)(data + off); |
1368 | if (inet_pton(AF_INET, argv[arg], RTA_DATA(rta))) { |
1369 | family = AF_INET; |
1370 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
1371 | rta->rta_len = RTA_LENGTH(4); |
1372 | } else if (inet_pton(AF_INET6, argv[arg], RTA_DATA(rta))) { |
1373 | family = AF_INET6; |
1374 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
1375 | rta->rta_len = RTA_LENGTH(16); |
1376 | } else { |
1377 | error(1, errno, "can't parse ip %s" , argv[arg]); |
1378 | } |
1379 | off += NLMSG_ALIGN(rta->rta_len); |
1380 | |
1381 | /* family */ |
1382 | rta = (void *)(data + off); |
1383 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
1384 | rta->rta_len = RTA_LENGTH(2); |
1385 | memcpy(RTA_DATA(rta), &family, 2); |
1386 | off += NLMSG_ALIGN(rta->rta_len); |
1387 | } |
1388 | |
1389 | if (++arg >= argc) |
1390 | error(1, 0, " missing flags keyword" ); |
1391 | |
1392 | for (; arg < argc; arg++) { |
1393 | if (!strcmp(argv[arg], "token" )) { |
1394 | if (++arg >= argc) |
1395 | error(1, 0, " missing token value" ); |
1396 | |
1397 | /* token */ |
1398 | token = strtoul(argv[arg], NULL, 10); |
1399 | } else if (!strcmp(argv[arg], "flags" )) { |
1400 | char *tok, *str; |
1401 | |
1402 | /* flags */ |
1403 | if (++arg >= argc) |
1404 | error(1, 0, " missing flags value" ); |
1405 | |
1406 | for (str = argv[arg]; (tok = strtok(str, "," )); |
1407 | str = NULL) { |
1408 | if (!strcmp(tok, "backup" )) |
1409 | flags |= MPTCP_PM_ADDR_FLAG_BACKUP; |
1410 | else if (!strcmp(tok, "fullmesh" )) |
1411 | flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; |
1412 | else if (strcmp(tok, "nobackup" ) && |
1413 | strcmp(tok, "nofullmesh" )) |
1414 | error(1, errno, |
1415 | "unknown flag %s" , argv[arg]); |
1416 | } |
1417 | |
1418 | rta = (void *)(data + off); |
1419 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
1420 | rta->rta_len = RTA_LENGTH(4); |
1421 | memcpy(RTA_DATA(rta), &flags, 4); |
1422 | off += NLMSG_ALIGN(rta->rta_len); |
1423 | } else if (!strcmp(argv[arg], "port" )) { |
1424 | u_int16_t port; |
1425 | |
1426 | if (use_id) |
1427 | error(1, 0, " port can't be used with id" ); |
1428 | |
1429 | if (++arg >= argc) |
1430 | error(1, 0, " missing port value" ); |
1431 | |
1432 | port = atoi(argv[arg]); |
1433 | rta = (void *)(data + off); |
1434 | rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
1435 | rta->rta_len = RTA_LENGTH(2); |
1436 | memcpy(RTA_DATA(rta), &port, 2); |
1437 | off += NLMSG_ALIGN(rta->rta_len); |
1438 | } else if (!strcmp(argv[arg], "rport" )) { |
1439 | if (++arg >= argc) |
1440 | error(1, 0, " missing remote port" ); |
1441 | |
1442 | rport = atoi(argv[arg]); |
1443 | } else if (!strcmp(argv[arg], "rip" )) { |
1444 | if (++arg >= argc) |
1445 | error(1, 0, " missing remote ip" ); |
1446 | |
1447 | rip = argv[arg]; |
1448 | } else { |
1449 | error(1, 0, "unknown keyword %s" , argv[arg]); |
1450 | } |
1451 | } |
1452 | nest->rta_len = off - nest_start; |
1453 | |
1454 | /* token */ |
1455 | if (token) { |
1456 | rta = (void *)(data + off); |
1457 | rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
1458 | rta->rta_len = RTA_LENGTH(4); |
1459 | memcpy(RTA_DATA(rta), &token, 4); |
1460 | off += NLMSG_ALIGN(rta->rta_len); |
1461 | } |
1462 | |
1463 | /* remote addr/port */ |
1464 | if (rip) { |
1465 | nest_start = off; |
1466 | nest = (void *)(data + off); |
1467 | nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR_REMOTE; |
1468 | nest->rta_len = RTA_LENGTH(0); |
1469 | off += NLMSG_ALIGN(nest->rta_len); |
1470 | |
1471 | /* addr data */ |
1472 | rta = (void *)(data + off); |
1473 | if (inet_pton(AF_INET, rip, RTA_DATA(rta))) { |
1474 | family = AF_INET; |
1475 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
1476 | rta->rta_len = RTA_LENGTH(4); |
1477 | } else if (inet_pton(AF_INET6, rip, RTA_DATA(rta))) { |
1478 | family = AF_INET6; |
1479 | rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
1480 | rta->rta_len = RTA_LENGTH(16); |
1481 | } else { |
1482 | error(1, errno, "can't parse ip %s" , (char *)rip); |
1483 | } |
1484 | off += NLMSG_ALIGN(rta->rta_len); |
1485 | |
1486 | /* family */ |
1487 | rta = (void *)(data + off); |
1488 | rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
1489 | rta->rta_len = RTA_LENGTH(2); |
1490 | memcpy(RTA_DATA(rta), &family, 2); |
1491 | off += NLMSG_ALIGN(rta->rta_len); |
1492 | |
1493 | if (rport) { |
1494 | rta = (void *)(data + off); |
1495 | rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
1496 | rta->rta_len = RTA_LENGTH(2); |
1497 | memcpy(RTA_DATA(rta), &rport, 2); |
1498 | off += NLMSG_ALIGN(rta->rta_len); |
1499 | } |
1500 | |
1501 | nest->rta_len = off - nest_start; |
1502 | } |
1503 | |
1504 | do_nl_req(fd, nh, len: off, max: 0); |
1505 | return 0; |
1506 | } |
1507 | |
1508 | int main(int argc, char *argv[]) |
1509 | { |
1510 | int events_mcast_grp; |
1511 | int pm_family; |
1512 | int fd; |
1513 | |
1514 | if (argc < 2) |
1515 | syntax(argv); |
1516 | |
1517 | fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); |
1518 | if (fd == -1) |
1519 | error(1, errno, "socket netlink" ); |
1520 | |
1521 | resolve_mptcp_pm_netlink(fd, pm_family: &pm_family, events_mcast_grp: &events_mcast_grp); |
1522 | |
1523 | if (!strcmp(argv[1], "add" )) |
1524 | return add_addr(fd, pm_family, argc, argv); |
1525 | else if (!strcmp(argv[1], "ann" )) |
1526 | return announce_addr(fd, pm_family, argc, argv); |
1527 | else if (!strcmp(argv[1], "rem" )) |
1528 | return remove_addr(fd, pm_family, argc, argv); |
1529 | else if (!strcmp(argv[1], "csf" )) |
1530 | return csf(fd, pm_family, argc, argv); |
1531 | else if (!strcmp(argv[1], "dsf" )) |
1532 | return dsf(fd, pm_family, argc, argv); |
1533 | else if (!strcmp(argv[1], "del" )) |
1534 | return del_addr(fd, pm_family, argc, argv); |
1535 | else if (!strcmp(argv[1], "flush" )) |
1536 | return flush_addrs(fd, pm_family, argc, argv); |
1537 | else if (!strcmp(argv[1], "get" )) |
1538 | return get_addr(fd, pm_family, argc, argv); |
1539 | else if (!strcmp(argv[1], "dump" )) |
1540 | return dump_addrs(fd, pm_family, argc, argv); |
1541 | else if (!strcmp(argv[1], "limits" )) |
1542 | return get_set_limits(fd, pm_family, argc, argv); |
1543 | else if (!strcmp(argv[1], "set" )) |
1544 | return set_flags(fd, pm_family, argc, argv); |
1545 | else if (!strcmp(argv[1], "events" )) |
1546 | return capture_events(fd, event_group: events_mcast_grp); |
1547 | else if (!strcmp(argv[1], "listen" )) |
1548 | return add_listener(argc, argv); |
1549 | |
1550 | fprintf(stderr, "unknown sub-command: %s" , argv[1]); |
1551 | syntax(argv); |
1552 | return 0; |
1553 | } |
1554 | |