Import upstream version 4.5.g4d02e74
Debian Janitor
1 year, 11 months ago
0 | ||
0 | 1 | # nsntrace |
1 | 2 | > Perform network trace of a single process by using network namespaces. |
2 | 3 | |
3 | This application uses Linux network namespaces to perform network traces of a single application. The traces are saved as pcap files. And can later be analyzed by for instance Wireshark. | |
4 | This application uses Linux network namespaces to perform a network trace of a single application. The trace is saved as a pcap file. And can later be analyzed by for instance Wireshark or tshark. | |
4 | 5 | |
5 | 6 | The nsntrace application is heavily inspired by the askbubuntu reply [here](https://askubuntu.com/a/499850). |
6 | 7 | And uses the same approach only confined to a single C program. |
7 | 8 | |
8 | What the application does is use the clone syscall to create a new | |
9 | network namespace (CLONE_NEWNET) and from that namespace launch the | |
10 | requested process as well as start a trace using libpcap. This will ensure that all | |
11 | the packets we trace come from the process. | |
9 | What the application does is use the clone syscall to create a new network namespace (CLONE_NEWNET) and from that namespace launch the requested process as well as start a trace using libpcap. This will ensure that all the packets we trace come from the process. | |
12 | 10 | |
13 | The problem we are left with is that the process is isolated in the | |
14 | namespace and cannot reach any other network. We get around that by | |
15 | creating virtual network interfaces. We keep one of them in the | |
16 | root network namespace and but the other one in the newly created one where | |
17 | our tracing takes place. We set the root namespaced one as the default gw | |
18 | of the trace namespaced virtual device. | |
11 | The problem we are left with is that the process is isolated in the namespace and cannot reach any other network. We get around that by creating virtual network interfaces. We keep one of them in the root network namespace and but the other one in the newly created one where our tracing takes place. We set the root namespaced one as the default gateway of the trace namespaced virtual device. | |
19 | 12 | |
20 | And then to make sure we can reach our indented net, we use ip | |
21 | tables and NAT to forward all traffic from the virtual device to our | |
22 | default network interface. | |
13 | And then to make sure we can reach our intended net, we use iptables and NAT to forward all traffic from the virtual device to our default network interface. | |
23 | 14 | |
24 | This will allow us to capture the packets from a single process while | |
25 | it is communicating with our default network. A limitation is that our | |
26 | ip address will be the NAT one of the virtual device. | |
15 | This will allow us to capture the packets from a single process while it is communicating with our default network. A limitation is that our ip address will be the NAT one of the virtual device. | |
27 | 16 | |
28 | Another limitation is, that since we are using iptables and since | |
29 | we are tracing raw sockets. This application needs to be run as root. | |
17 | Another limitation is, that since we are using iptables and since we are tracing raw sockets. This application needs to be run as root. | |
30 | 18 | |
31 | On many systems today the nameserver functionality is handled by an | |
32 | application such as systemd-resolved or dnsmasq and the nameserver | |
33 | address is a loopback address (like 127.0.0.53) where that application | |
34 | listens for incoming DNS queries. | |
19 | On many systems today the nameserver functionality is handled by an application such as systemd-resolved or dnsmasq and the nameserver address is a loopback address (like 127.0.0.53) where that application listens for incoming DNS queries. | |
35 | 20 | |
36 | This will not work for us in this network namespace environment, since | |
37 | we have our own namespaced loopback device. To work around this one can use the | |
38 | `--use-public-dns` option to override resolv.conf in the namespace. Then nsntrace | |
39 | will use nameservers from Quad9 (9.9.9.9), Cloudflare (1.1.1.1), Google (8.8.8.8) and | |
40 | OpenDNS (208.67.222.222) to perform DNS queries. | |
21 | This will not work for us in this network namespace environment, since we have our own namespaced loopback device. To work around this one can use the `--use-public-dns` option to override resolv.conf in the namespace. Then nsntrace will use nameservers from Quad9 (9.9.9.9), Cloudflare (1.1.1.1), Google (8.8.8.8) and OpenDNS (208.67.222.222) to perform DNS queries. | |
41 | 22 | |
42 | 23 | ## usage |
43 | 24 | $ nsntrace |
97 | 78 | 13 0.113167 142.250.74.36 → 172.16.42.255 TCP 66 80 → 51088 [FIN, ACK] Seq=13815 Ack=143 Win=66816 Len=0 TSval=2846449554 TSecr=1362504583 |
98 | 79 | 14 0.113190 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [ACK] Seq=143 Ack=13816 Win=64128 Len=0 TSval=1362504595 TSecr=2846449554 |
99 | 80 | ``` |
81 | ||
82 | # building from source | |
83 | To build nsntrace from source the following steps are needed: | |
84 | ||
85 | ``` | |
86 | $ ./autogen.sh | |
87 | $ ./configure | |
88 | $ make | |
89 | ``` | |
90 | And to install: | |
91 | ``` | |
92 | $ sudo make install | |
93 | ``` | |
94 | ||
95 | ## dependencies | |
96 | The packages needed to build nsntrace are (Debian/Ubuntu style naming): | |
97 | * automake | |
98 | * docbook-xml | |
99 | * docbook-xsl | |
100 | * iptables | |
101 | * libnl-route-3-dev | |
102 | * lippcap-dev | |
103 | * pkg-config | |
104 | * xsltproc |
54 | 54 | * to interface with the Netlink API and perform network configuration. |
55 | 55 | */ |
56 | 56 | |
57 | #define IP_BASE "172.16.42" | |
58 | #define NS_IP IP_BASE ".255" | |
59 | #define GW_IP IP_BASE ".254" | |
60 | #define NS_IP_RANGE NS_IP "/31" | |
61 | #define GW_IP_RANGE GW_IP "/31" | |
62 | ||
63 | #define IF_BASE "nsntrace" | |
64 | #define NS_IF IF_BASE "-netns" | |
65 | #define GW_IF IF_BASE | |
57 | #define NS_IF_BASE "nscap" | |
58 | #define GW_IF_BASE "nsgw" | |
59 | ||
60 | ||
66 | 61 | |
67 | 62 | static struct nl_sock * |
68 | 63 | _nsntrace_net_get_nl_socket() { |
292 | 287 | _nsntrace_net_create_resolv_conf() |
293 | 288 | { |
294 | 289 | int fd; |
290 | char resolv_path[PATH_MAX] = { 0, }; | |
295 | 291 | const char *resolv = "nameserver 9.9.9.9\n" /* Quad9 */ |
296 | 292 | "nameserver 1.1.1.1\n" /* Cloudflare */ |
297 | 293 | "nameserver 8.8.8.8\n" /* Google */ |
298 | 294 | "nameserver 208.67.222.222\n"; /* OpenDNS */ |
299 | 295 | |
300 | if (mkdir(NSNTRACE_RUN_DIR, 0644) < 0) { | |
301 | if (errno != EEXIST) { | |
302 | perror("mkdir"); | |
303 | return; | |
304 | } | |
305 | } | |
306 | ||
307 | char resolv_path[] = NSNTRACE_RUN_DIR "/resolv.confXXXXXX"; | |
296 | ||
297 | snprintf(resolv_path, PATH_MAX, "%s/%d/resolv.confXXXXXX", | |
298 | NSNTRACE_RUN_DIR, getppid()); | |
308 | 299 | if ((fd = mkstemp(resolv_path)) < 0) { |
309 | 300 | perror("mkstemp"); |
310 | 301 | return; |
393 | 384 | } |
394 | 385 | |
395 | 386 | /* |
387 | * Attempt to find a free ip base (172.19.x) to use. We use randomization | |
388 | * and check addresses that are already bound to our devices, this is to | |
389 | * enable us to run multiple instances of nsntrace. | |
390 | */ | |
391 | static const char * | |
392 | _nsntrace_net_get_ip_base() | |
393 | { | |
394 | char buf[1024]; | |
395 | char ip[16]; | |
396 | struct ifconf ifc; | |
397 | struct timeval tval; | |
398 | int s; | |
399 | int i; | |
400 | ||
401 | s = socket(AF_INET, SOCK_DGRAM, 0); | |
402 | if(s < 0) { | |
403 | perror("socket"); | |
404 | return NULL; | |
405 | } | |
406 | ||
407 | gettimeofday(&tval, NULL); | |
408 | srand(tval.tv_usec); | |
409 | while (1) { | |
410 | int digit = rand() % 255; | |
411 | snprintf(ip, 16, "172.19.%d", digit); | |
412 | ||
413 | /* query available interfaces. */ | |
414 | ifc.ifc_len = sizeof(buf); | |
415 | ifc.ifc_buf = buf; | |
416 | if(ioctl(s, SIOCGIFCONF, &ifc) < 0) | |
417 | { | |
418 | perror("ioctl"); | |
419 | return NULL; | |
420 | } | |
421 | ||
422 | /* iterate through the list of interfaces. */ | |
423 | int found = 0; | |
424 | for(i = 0; i < (ifc.ifc_len / sizeof(struct ifreq)); i++) | |
425 | { | |
426 | struct ifreq *item = &ifc.ifc_req[i]; | |
427 | char *addr = inet_ntoa(((struct sockaddr_in *)&item->ifr_addr)->sin_addr); | |
428 | ||
429 | if (!strncmp(addr, ip, strlen(ip))) { | |
430 | found = 1; | |
431 | break; | |
432 | } | |
433 | } | |
434 | if (found == 0) { | |
435 | return strdup(ip); | |
436 | } | |
437 | } | |
438 | return NULL; | |
439 | } | |
440 | ||
441 | int | |
442 | nsntrace_net_get_if_info(pid_t pid, | |
443 | struct nsntrace_if_info *info) | |
444 | { | |
445 | const char *ip_base = _nsntrace_net_get_ip_base(); | |
446 | if (!ip_base) { | |
447 | fprintf(stderr, "failed to allocate IP address"); | |
448 | return -1; | |
449 | } | |
450 | ||
451 | snprintf(info->ns_if, IFNAMSIZ, "%s%d", NS_IF_BASE, pid); | |
452 | snprintf(info->gw_if, IFNAMSIZ, "%s%d", GW_IF_BASE, pid); | |
453 | snprintf(info->ns_ip, IP_ADDR_LEN, "%s.255", ip_base); | |
454 | snprintf(info->gw_ip, IP_ADDR_LEN, "%s.254", ip_base); | |
455 | snprintf(info->ns_ip_range, IP_ADDR_LEN + 3, "%s/31", info->ns_ip); | |
456 | snprintf(info->gw_ip_range, IP_ADDR_LEN + 3, "%s/31", info->gw_ip); | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
461 | /* | |
396 | 462 | * Set up the environment needed from the root network namespace point |
397 | 463 | * of view. Create virtual ethernet interface (see above) and set our side |
398 | 464 | * of it up and set address. |
401 | 467 | */ |
402 | 468 | int |
403 | 469 | nsntrace_net_init(pid_t ns_pid, |
404 | const char *device) | |
470 | const char *device, | |
471 | struct nsntrace_if_info *info) | |
405 | 472 | { |
406 | 473 | int ret = 0; |
407 | 474 | |
408 | if ((ret = _nsntrace_net_create_veth(GW_IF, NS_IF, ns_pid)) < 0) { | |
409 | return ret; | |
410 | } | |
411 | ||
412 | if ((ret = _nsntrace_net_iface_up(GW_IF, GW_IP_RANGE, NULL)) < 0) { | |
413 | return ret; | |
414 | } | |
415 | ||
416 | if ((ret = _nsntrace_net_set_nat(GW_IP_RANGE, GW_IF, device, 1)) < 0) { | |
475 | if ((ret = _nsntrace_net_create_veth(info->gw_if, info->ns_if, ns_pid)) < 0) { | |
476 | return ret; | |
477 | } | |
478 | ||
479 | if ((ret = _nsntrace_net_iface_up(info->gw_if, info->gw_ip_range, NULL)) < 0) { | |
480 | return ret; | |
481 | } | |
482 | ||
483 | if ((ret = _nsntrace_net_set_nat(info->gw_ip_range, info->gw_if, device, 1)) < 0) { | |
417 | 484 | return ret; |
418 | 485 | } |
419 | 486 | |
425 | 492 | * Teardown the temporary network trickery we created in init. |
426 | 493 | */ |
427 | 494 | int |
428 | nsntrace_net_deinit(const char *device) | |
495 | nsntrace_net_deinit(pid_t ns_pid, | |
496 | const char *device, | |
497 | struct nsntrace_if_info *info) | |
429 | 498 | { |
430 | 499 | int ret = 0; |
431 | 500 | |
432 | if ((ret = _nsntrace_net_set_nat(GW_IP_RANGE, GW_IF, device, 0)) < 0) { | |
433 | return ret; | |
434 | } | |
435 | ||
436 | if ((ret = _nsntrace_net_iface_delete(NS_IF)) < 0) { | |
437 | return ret; | |
438 | } | |
439 | ||
440 | if ((ret = _nsntrace_net_iface_delete(GW_IF)) < 0) { | |
501 | if ((ret = _nsntrace_net_set_nat(info->gw_ip_range, info->gw_if, device, 0)) < 0) { | |
502 | return ret; | |
503 | } | |
504 | ||
505 | if ((ret = _nsntrace_net_iface_delete(info->ns_if)) < 0) { | |
506 | return ret; | |
507 | } | |
508 | ||
509 | if ((ret = _nsntrace_net_iface_delete(info->gw_if)) < 0) { | |
441 | 510 | return ret; |
442 | 511 | } |
443 | 512 | |
448 | 517 | * Set up the namespaced net infrastructure needed. |
449 | 518 | */ |
450 | 519 | int |
451 | nsntrace_net_ns_init(int use_public_dns) | |
520 | nsntrace_net_ns_init(int use_public_dns, | |
521 | struct nsntrace_if_info *info) | |
452 | 522 | { |
453 | 523 | int ret = 0; |
454 | 524 | |
455 | if ((ret = _nsntrace_net_iface_up(NS_IF, NS_IP_RANGE, GW_IP)) < 0) { | |
525 | if ((ret = _nsntrace_net_iface_up(info->ns_if, info->ns_ip_range, info->gw_ip)) < 0) { | |
456 | 526 | return EXIT_FAILURE; |
457 | 527 | } |
458 | 528 | |
490 | 560 | |
491 | 561 | return ch == '1'; |
492 | 562 | } |
493 | ||
494 | const char * | |
495 | nsntrace_net_get_capture_interface() | |
496 | { | |
497 | return NS_IF; | |
498 | } | |
499 | ||
500 | const char * | |
501 | nsntrace_net_get_capture_ip() | |
502 | { | |
503 | return NS_IP; | |
504 | } |
15 | 15 | #ifndef _NSNTRACE_NET_H_ |
16 | 16 | #define _NSNTRACE_NET_H |
17 | 17 | |
18 | #include <linux/if.h> | |
19 | ||
20 | #define IP_ADDR_LEN 16 // 4 sets of 3 numbers each separater by a dot + '\0' | |
18 | 21 | #define NSNTRACE_RUN_DIR "/run/nsntrace" |
19 | 22 | |
23 | struct nsntrace_if_info { | |
24 | char ns_if[IFNAMSIZ]; | |
25 | char gw_if[IFNAMSIZ]; | |
26 | char ns_ip[16]; | |
27 | char gw_ip[16]; | |
28 | char ns_ip_range[19]; | |
29 | char gw_ip_range[19]; | |
30 | }; | |
31 | ||
20 | 32 | int nsntrace_net_init(pid_t ns_pid, |
21 | const char *device); | |
33 | const char *device, | |
34 | struct nsntrace_if_info *info); | |
22 | 35 | |
23 | int nsntrace_net_deinit(const char *device); | |
36 | int nsntrace_net_deinit(pid_t ns_pid, | |
37 | const char *device, | |
38 | struct nsntrace_if_info *info); | |
24 | 39 | |
25 | int nsntrace_net_ns_init(int use_public_dns); | |
40 | int nsntrace_net_ns_init(int use_public_dns, | |
41 | struct nsntrace_if_info *info); | |
26 | 42 | |
27 | 43 | int nsntrace_net_ip_forward_enabled(); |
28 | 44 | |
29 | const char *nsntrace_net_get_capture_ip(); | |
30 | ||
31 | const char *nsntrace_net_get_capture_interface(); | |
45 | int nsntrace_net_get_if_info(pid_t pid, | |
46 | struct nsntrace_if_info *info); | |
32 | 47 | |
33 | 48 | #endif |
81 | 81 | char * const *args; |
82 | 82 | }; |
83 | 83 | |
84 | struct nsntrace_common { | |
85 | struct nsntrace_options *options; | |
86 | struct nsntrace_if_info if_info; | |
87 | }; | |
88 | ||
84 | 89 | #define PUBLIC_DNS 1000 |
85 | 90 | static const char *short_opt = "+o:d:u:f:h"; |
86 | 91 | static struct option long_opt[] = { |
145 | 150 | return rv; |
146 | 151 | } |
147 | 152 | |
153 | static void | |
154 | _nsntrace_remove_rundir(pid_t pid) | |
155 | { | |
156 | char path[PATH_MAX] = { 0, }; | |
157 | ||
158 | snprintf(path, PATH_MAX, "%s/%d", NSNTRACE_RUN_DIR, getppid()); | |
159 | nftw(path, _nsntrace_unlink_cb, 64, FTW_DEPTH | FTW_PHYS); | |
160 | } | |
161 | ||
148 | 162 | static void _nsntrace_remove_ns() |
149 | 163 | { |
150 | 164 | char netns_path[PATH_MAX]; |
156 | 170 | } |
157 | 171 | |
158 | 172 | /* clean up run-time files */ |
159 | nftw(NSNTRACE_RUN_DIR, _nsntrace_unlink_cb, 64, FTW_DEPTH | FTW_PHYS); | |
173 | _nsntrace_remove_rundir(getppid()); | |
160 | 174 | } |
161 | 175 | |
162 | 176 | static void |
225 | 239 | } |
226 | 240 | |
227 | 241 | static void |
228 | _nsntrace_start_tracer(struct nsntrace_options *options) | |
242 | _nsntrace_start_tracer(struct nsntrace_options *options, | |
243 | struct nsntrace_if_info *info) | |
229 | 244 | { |
230 | 245 | int ret; |
231 | const char *ip; | |
232 | const char *interface; | |
233 | ||
234 | 246 | /* |
235 | 247 | * If outfile is a lone dash ("-"), the user wants us to output to STDOUT, |
236 | 248 | * in that case we avoid printing anything else to STDOUT and use STDERR |
249 | 261 | } |
250 | 262 | } |
251 | 263 | |
252 | ip = nsntrace_net_get_capture_ip(); | |
253 | interface = nsntrace_net_get_capture_interface(); | |
254 | ||
255 | 264 | fprintf(where, "Starting network trace of '%s' on interface %s.\n" |
256 | 265 | "Your IP address in this trace is %s.\n" |
257 | 266 | "Use ctrl-c to end at any time.\n\n", |
258 | options->args[0], options->device, ip); | |
259 | ret = nsntrace_capture_start(interface, options->filter, fp); | |
267 | options->args[0], options->device, info->ns_ip); | |
268 | ret = nsntrace_capture_start(info->ns_if, options->filter, fp); | |
260 | 269 | if (ret != 0) { |
261 | 270 | exit(ret); |
262 | 271 | } |
343 | 352 | netns_main(void *arg) { |
344 | 353 | int status; |
345 | 354 | int ret = EXIT_SUCCESS; |
346 | struct nsntrace_options *options = (struct nsntrace_options *) arg; | |
347 | ||
348 | if (nsntrace_net_ns_init(options->use_public_dns) < 0) { | |
355 | struct nsntrace_common *common = (struct nsntrace_common *) arg; | |
356 | struct nsntrace_options *options = common->options; | |
357 | ||
358 | if (nsntrace_net_ns_init(options->use_public_dns, &common->if_info) < 0) { | |
349 | 359 | fprintf(stderr, "failed to setup network environment\n"); |
350 | 360 | return EXIT_FAILURE; |
351 | 361 | } |
352 | 362 | |
353 | 363 | _nsntrace_handle_signals(_nsntrace_cleanup_ns_signal_callback); |
354 | _nsntrace_start_tracer(options); | |
364 | _nsntrace_start_tracer(common->options, &common->if_info); | |
355 | 365 | |
356 | 366 | _nsntrace_set_name(); |
357 | 367 | |
383 | 393 | _nsntrace_cleanup_ns(); |
384 | 394 | exit(ret); |
385 | 395 | } else { /* child - tracee */ |
386 | _nsntrace_start_tracee(options); | |
396 | _nsntrace_start_tracee(common->options); | |
387 | 397 | } |
388 | 398 | |
389 | 399 | return ret; |
462 | 472 | } |
463 | 473 | } |
464 | 474 | |
475 | /* | |
476 | * Create the pid based run dir for this nsntrace instance. | |
477 | * This is for storing run-time files and to be able to cleanup | |
478 | * easily without removing other nsntrace instances files. | |
479 | */ | |
480 | static void | |
481 | _nsntrace_mkrundir() | |
482 | { | |
483 | char path[PATH_MAX] = { 0, }; | |
484 | ||
485 | if (mkdir(NSNTRACE_RUN_DIR, 0644) < 0) { | |
486 | if (errno != EEXIST) { | |
487 | perror("mkdir"); | |
488 | } | |
489 | } | |
490 | snprintf(path, PATH_MAX, "%s/%d", NSNTRACE_RUN_DIR, getpid()); | |
491 | if (mkdir(path, 0644) < 0) { | |
492 | if (errno != EEXIST) { | |
493 | perror("mkdir"); | |
494 | } | |
495 | } | |
496 | } | |
497 | ||
465 | 498 | int |
466 | 499 | main(int argc, char **argv) |
467 | 500 | { |
468 | 501 | struct nsntrace_options options = { 0 }; |
469 | pid_t pid; | |
502 | struct nsntrace_common common = { &options, }; | |
503 | pid_t pid = 0; | |
470 | 504 | int status; |
471 | 505 | int ret = EXIT_SUCCESS; |
472 | 506 | |
489 | 523 | exit(EXIT_FAILURE); |
490 | 524 | } |
491 | 525 | |
526 | _nsntrace_mkrundir(); | |
527 | ||
528 | if (nsntrace_net_get_if_info(getpid(), &common.if_info) < 0) { | |
529 | ret = EXIT_FAILURE; | |
530 | goto out; | |
531 | } | |
532 | ||
492 | 533 | /* here we create a new process in a new network namespace */ |
493 | 534 | pid = clone(netns_main, child_stack + STACK_SIZE, |
494 | CLONE_NEWNET | SIGCHLD, &options); | |
535 | CLONE_NEWNET | SIGCHLD, &common); | |
495 | 536 | if (pid < 0) { |
496 | fprintf(stderr, "clone failed\n"); | |
497 | exit(EXIT_FAILURE); | |
537 | perror("clone"); | |
538 | ret = EXIT_FAILURE; | |
539 | goto out; | |
498 | 540 | } |
499 | 541 | |
500 | 542 | _nsntrace_handle_signals(_nsntrace_cleanup); |
501 | 543 | |
502 | if ((ret = nsntrace_net_init(pid, options.device)) < 0 || | |
503 | (ret = nsntrace_capture_check_device(options.device))) { | |
544 | if ((ret = nsntrace_net_init(pid, options.device, &common.if_info)) < 0 || | |
545 | (ret = nsntrace_capture_check_device(options.device))) { | |
504 | 546 | fprintf(stderr, "Failed to setup networking environment\n"); |
505 | 547 | kill(pid, SIGKILL); |
506 | 548 | goto out; |
515 | 557 | } |
516 | 558 | |
517 | 559 | out: |
518 | nsntrace_net_deinit(options.device); | |
560 | if (pid != 0) { | |
561 | nsntrace_net_deinit(pid, options.device, &common.if_info); | |
562 | } | |
563 | _nsntrace_remove_rundir(getpid()); | |
519 | 564 | return ret; |
520 | 565 | } |
0 | 0 | #!/bin/sh |
1 | 1 | |
2 | 2 | # HUP INT QUIT ABRT SEGV TERM |
3 | signals='1 2 3 6 11 15' | |
3 | set -- 1 2 3 6 11 15 | |
4 | 4 | timeout=1 |
5 | 5 | ip="172.16.42.254" |
6 | 6 | if=nsntrace |
9 | 9 | # sleep to allow for cleanup after signal |
10 | 10 | sleep 1 |
11 | 11 | |
12 | (sudo iptables -w -t nat -L | grep $ip) && { | |
12 | (sudo iptables -w -t nat -L | grep "$ip") && { | |
13 | 13 | echo "Rules not cleaned up after signal $signal" |
14 | 14 | exit 1 |
15 | 15 | } |
16 | 16 | |
17 | (sudo ip link | grep $if) && { | |
17 | (sudo ip link | grep "$if") && { | |
18 | 18 | echo "Link not cleaned up after signal $signal" |
19 | 19 | exit 1 |
20 | 20 | } |
21 | 21 | |
22 | ls /run/nsntrace > /dev/null 2>&1 && { | |
22 | ls "/run/nsntrace/$1" > /dev/null 2>&1 && { | |
23 | 23 | echo "run-time files not cleaned up after signal $signal" |
24 | 24 | exit 1 |
25 | 25 | } |
26 | 26 | |
27 | rm -rf *.pcap | |
27 | rm -f -- *.pcap | |
28 | 28 | } |
29 | 29 | |
30 | 30 | start_and_kill() { |
31 | local signal=$1 | |
31 | sig="$1" | |
32 | 32 | |
33 | 33 | sudo ../src/nsntrace ./test_program_dummy.sh & |
34 | sleep $timeout | |
34 | sleep "$timeout" | |
35 | 35 | pid=$(pidof nsntrace) |
36 | sudo kill -$signal $pid | |
36 | sudo kill "-$sig" "$pid" | |
37 | 37 | |
38 | check_cleanup | |
38 | check_cleanup "$pid" | |
39 | unset sig | |
39 | 40 | } |
40 | 41 | |
41 | for signal in $signals; do | |
42 | start_and_kill $signal | |
42 | for signal ; do | |
43 | start_and_kill "$signal" | |
43 | 44 | done |
44 | 45 | |
46 | sudo ../src/nsntrace ./test_program_dummy_ends.sh | |
47 | check_cleanup "$!" | |
48 | ||
45 | 49 | exit 0 |
0 | #!/bin/sh | |
1 | ||
2 | num_packets=10 | |
3 | ||
4 | launch_nsntrace() | |
5 | { | |
6 | id="$1" | |
7 | filter="icmp[icmptype]==icmp-echo" | |
8 | ||
9 | sudo ../src/nsntrace -f "$filter" --use-public-dns -o "$id.pcap" ping -c "$num_packets" google.com > /dev/null 2>&1 & | |
10 | sleep 1.0e-3 | |
11 | ||
12 | unset id filter | |
13 | } | |
14 | ||
15 | for i in $(seq 5); do | |
16 | launch_nsntrace "$i" | |
17 | done | |
18 | ||
19 | sleep 20 | |
20 | ||
21 | for i in $(seq 5); do | |
22 | captured=$(tshark -r "$i.pcap" | wc -l) | |
23 | [ "$captured" = "$num_packets" ] || { | |
24 | echo "failed to capture all packets" | |
25 | exit 1 | |
26 | } | |
27 | done | |
28 | ||
29 | rm -f -- *.pcap | |
30 | exit 0 |
0 | 0 | #!/bin/sh |
1 | 1 | |
2 | 2 | packets=99 |
3 | cmd="./udp_send 1337 $packets" | |
4 | 3 | filter="udp port 1337" |
5 | 4 | |
6 | 5 | # start sending packages non-namespaced |
7 | 6 | ./udp_send 1337 -1 & |
8 | 7 | |
9 | 8 | # stop the non-namespaced udp_send on exit |
10 | trap "kill $(pidof udp_send)" EXIT | |
9 | pidof_udp_send=$(pidof udp_send) | |
10 | trap 'kill "$pidof_udp_send"' EXIT | |
11 | 11 | |
12 | 12 | # make sure we only capture the packets from the namespaced version |
13 | sudo ../src/nsntrace -d lo -f "$filter" $cmd | grep "$packets packets" || { | |
13 | sudo ../src/nsntrace -d lo -f "$filter" ./udp_send 1337 "$packets" | grep "$packets packets" || { | |
14 | 14 | echo "Did not capture $packets packets!" |
15 | 15 | exit 1 |
16 | 16 | } |
26 | 26 | check_return_code 1 -o /path/does/not/exist /bin/true |
27 | 27 | check_return_code 1 -d invalid_device /bin/true |
28 | 28 | |
29 | rm -rf *.pcap | |
29 | rm -f -- *.pcap | |
30 | 30 | |
31 | 31 | exit ${RET} |
10 | 10 | int main(int argc, char **argv) |
11 | 11 | { |
12 | 12 | struct sockaddr_in addr = { 0 }; |
13 | int port, num, s; | |
13 | int port, num, s, interval = 0; | |
14 | 14 | |
15 | if (argc != 3) { | |
16 | fprintf(stderr, "usage: udp_send port num_packets\n"); | |
15 | if (argc < 3) { | |
16 | fprintf(stderr, "usage: udp_send port num_packets [interval]\n"); | |
17 | 17 | exit(EXIT_FAILURE); |
18 | 18 | } |
19 | 19 | port = atoi(argv[1]); |
20 | 20 | num = atoi(argv[2]); |
21 | ||
22 | if (argc > 3) { | |
23 | interval = atoi(argv[3]); | |
24 | } | |
21 | 25 | |
22 | 26 | if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
23 | 27 | fprintf(stderr, "failed to create socket\n"); |
38 | 42 | fprintf(stderr, "failed to send message\n"); |
39 | 43 | exit(EXIT_FAILURE); |
40 | 44 | } |
45 | ||
46 | sleep(interval); | |
41 | 47 | } |
42 | 48 | |
43 | 49 | return 0; |