Advertisement
Guest User

Untitled

a guest
Apr 25th, 2013
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.11 KB | None | 0 0
  1. /*
  2. * threesome.c -- test how different protocols manage with veth
  3. *
  4. * This program creates a pair of veth network devices, configures them,
  5. * and optionally puts one of the pair into an isolated network space.
  6. * Then it either pings one side from the other or sends messages over
  7. * TCP or SCTP likewise. Both IPv4 and IPv6 are supported. You need
  8. * to run this program with superuser privileges.
  9. *
  10. * Compile as: cc -Wall threesome.c -lsctp -o threesome
  11. *
  12. * Usage:
  13. * threesome [-N46i] [-p {ping|tcp|sctp}]
  14. * [-m <master-address>] [-s <slave-address>]
  15. * [-P <port>]
  16. *
  17. * Options:
  18. * -N Do not put one of the veth devices
  19. * to another network space.
  20. * -4 Use IPv4 (default).
  21. * -6 Use IPv6.
  22. * -m <master-address> IP address of veth0. You can only specify
  23. * IPv4 address, because the IPv6 address is
  24. * generated by veth at the time both devices
  25. * are pulled up. The default is 1.2.3.4.
  26. * -s <slave-address> IP address of the device which is put into
  27. * a separate namespace. Default is 1.2.3.5.
  28. * -P <port> The port of the server side.
  29. * -i By default, we communicate from the master
  30. * side (eg. we ping the slave from the master).
  31. * With this flag you can switch the roles.
  32. */
  33.  
  34. /* Include files */
  35. #include <stdlib.h>
  36. #include <stdarg.h>
  37. #include <unistd.h>
  38. #include <assert.h>
  39. #include <stdio.h>
  40. #include <string.h>
  41. #include <sched.h>
  42. #include <arpa/inet.h>
  43. #include <netinet/in.h>
  44. #include <net/if.h>
  45. #include <sys/time.h>
  46. #include <sys/wait.h>
  47. #include <sys/ioctl.h>
  48.  
  49. // Maximum size of an IPv6 address in hexadecimal representation.
  50. #define IPV6_ADDRLEN (4*8 + 7 + 4 + 1)
  51.  
  52. // Either "main", "master" or "slave".
  53. static const char *Whoami = "main";
  54.  
  55. /* Program code */
  56. // Like popen(3), except that it has an execlp(3)-like interface.
  57. // NOTE the child is not reaped when @st is closed.
  58. static FILE *__attribute__((sentinel))
  59. cmd_pipe(char *prg, ...)
  60. {
  61. int childs_stdout[2];
  62. unsigned n, i;
  63. va_list args;
  64. char **argv;
  65. FILE *st;
  66.  
  67. // Make new file descriptor.
  68. assert(!pipe(childs_stdout));
  69. assert((st = fdopen(childs_stdout[0], "r")) != NULL);
  70. if (fork())
  71. {
  72. close(childs_stdout[1]);
  73. return st;
  74. } else
  75. {
  76. fclose(st);
  77. assert(dup2(childs_stdout[1], STDOUT_FILENO)
  78. == STDOUT_FILENO);
  79. }
  80.  
  81. // Count the number of parameters.
  82. va_start(args, prg);
  83. for (n = 0; va_arg(args, char *); n++)
  84. ;
  85. va_end(args);
  86.  
  87. // Copy the arguments to argv.
  88. assert((argv = malloc(sizeof(*argv) * (1+n+1))) != NULL);
  89. va_start(args, prg);
  90. argv[0] = prg;
  91. printf("%6s: %s", Whoami, argv[0]);
  92. for (i = 1; n > 0; n--, i++)
  93. { // Print the command line.
  94. argv[i] = va_arg(args, char *);
  95. printf(" %s", argv[i]);
  96. }
  97. puts(" |");
  98. argv[i] = NULL;
  99. va_end(args);
  100.  
  101. assert(!execvp(argv[0], argv));
  102. abort();
  103. }
  104.  
  105. // Just like system(3) but with execlp(3) syntax.
  106. static void __attribute__((sentinel))
  107. cmd(char *prg, ...)
  108. {
  109. pid_t child;
  110.  
  111. if (!(child = fork()))
  112. {
  113. unsigned n, i;
  114. va_list args;
  115. char **argv;
  116.  
  117. // Count the number of parameters.
  118. va_start(args, prg);
  119. for (n = 0; va_arg(args, char *); n++)
  120. ;
  121. va_end(args);
  122.  
  123. // Copy the arguments to argv.
  124. assert((argv = malloc(sizeof(*argv) * (1+n+1))) != NULL);
  125. va_start(args, prg);
  126. argv[0] = prg;
  127. printf("%6s: %s", Whoami, argv[0]);
  128. for (i = 1; n > 0; n--, i++)
  129. { // Print the command line.
  130. argv[i] = va_arg(args, char *);
  131. printf(" %s", argv[i]);
  132. }
  133. putchar('\n');
  134. argv[i] = NULL;
  135. va_end(args);
  136.  
  137. assert(!execvp(argv[0], argv));
  138. } else
  139. {
  140. int status;
  141.  
  142. // Wait until the child finishes.
  143. while (waitpid(child, &status, 0) > 0)
  144. assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);
  145. }
  146. }
  147.  
  148. // Logging
  149. static void __attribute__((noreturn, format(printf, 1, 2)))
  150. error(const char *fmt, ...)
  151. {
  152. va_list args;
  153.  
  154. fprintf(stderr, "%6s: ", Whoami);
  155. va_start(args, fmt);
  156. vfprintf(stderr, fmt, args);
  157. va_end(args);
  158. fputc('\n', stderr);
  159. exit(1);
  160. }
  161.  
  162. static void __attribute__((format(printf, 1, 2)))
  163. logit(const char *fmt, ...)
  164. {
  165. va_list args;
  166.  
  167. printf("%6s: ", Whoami);
  168. va_start(args, fmt);
  169. vprintf(fmt, args);
  170. va_end(args);
  171. putchar('\n');
  172. }
  173.  
  174. static void __attribute__((noreturn))
  175. usage(const char *prg)
  176. {
  177. const char *fname;
  178.  
  179. if (!(fname = strrchr(prg, '/')))
  180. fname = prg;
  181. else
  182. fname = prg+1;
  183.  
  184. printf("%s [-N] [-46] [-p {ping|tcp|sctp}] [-m <master's-ip>] "
  185. "[-s <slave's ip>] [-i]\n", fname);
  186. exit(1);
  187. }
  188.  
  189. // Retrieve the IPv6 address of @iface.
  190. static void get_ipv6_address(char ip[IPV6_ADDRLEN], const char *iface)
  191. {
  192. FILE *st;
  193. char line[128];
  194.  
  195. st = cmd_pipe("ifconfig", iface, NULL);
  196. while (fgets(line, sizeof(line), st))
  197. {
  198. char *p;
  199.  
  200. // inet6 addr: fe80::243a:1aff:fe7d:4cf0/64 Scope:Link
  201. if (sscanf(line, " inet6 addr: %s", ip) < 1)
  202. continue;
  203. if ((p = strrchr(ip, '/')) != NULL)
  204. *p = '\0';
  205. fclose(st);
  206. return;
  207. }
  208. abort();
  209. }
  210.  
  211. // Populate a sockaddr_in with @ip and @port.
  212. static void fill_saddr4(struct sockaddr_in *saddr,
  213. const char *ip, unsigned port)
  214. {
  215. memset(saddr, 0, sizeof(*saddr));
  216. saddr->sin_family = AF_INET;
  217. assert(inet_pton(AF_INET, ip, &saddr->sin_addr));
  218. saddr->sin_port = htons(port);
  219. }
  220.  
  221. // Populate a sockaddr_in6 with @ip and @port.
  222. static void fill_saddr6(int sfd, struct sockaddr_in6 *saddr,
  223. const char *iface, const char *ip, unsigned port)
  224. {
  225. struct ifreq iface_info;
  226.  
  227. memset(saddr, 0, sizeof(*saddr));
  228. saddr->sin6_family = AF_INET6;
  229. assert(inet_pton(AF_INET6, ip, &saddr->sin6_addr) == 1);
  230. saddr->sin6_port = htons(port);
  231.  
  232. // Bind to @iface once again (for SCTP).
  233. logit("setting scope id to %s", iface);
  234. memset(&iface_info, 0, sizeof(iface_info));
  235. strcpy(iface_info.ifr_name, iface);
  236. assert(!ioctl(sfd, SIOCGIFINDEX, &iface_info));
  237. saddr->sin6_scope_id = iface_info.ifr_ifindex;
  238. }
  239.  
  240. // Prepare a socket: create it, set socket options, bind() and listen()
  241. // if !@is_client.
  242. static int prepare(int is_master, unsigned ip_version, const char *proto,
  243. const char *ip, unsigned dst_port, int is_client)
  244. {
  245. int sfd, on;
  246. const char *iface;
  247.  
  248. // The purpose of sleeping here is unknown, but if omitted,
  249. // IPv6 doesn't work properly.
  250. if (ip_version == 6 && !is_client)
  251. {
  252. struct timeval tv;
  253.  
  254. gettimeofday(&tv, NULL);
  255. logit("sleeping for 2 seconds (%lu.%6lu)",
  256. tv.tv_sec, tv.tv_usec);
  257. sleep(2);
  258. gettimeofday(&tv, NULL);
  259. logit("woken up (%lu.%6lu)", tv.tv_sec, tv.tv_usec);
  260. }
  261.  
  262. if (!strcmp(proto, "ping"))
  263. return -1;
  264.  
  265. iface = is_master ? "veth0" : "veth1";
  266.  
  267. // Create the socket.
  268. logit("socket(%s, SOCK_STREAM, %s)",
  269. ip_version == 4 ? "PF_INET" : "PF_INET6",
  270. !strcmp(proto, "tcp") ? "IPPROTO_TCP" : "IPPROTO_SCTP");
  271. if ((sfd = socket(ip_version == 4 ? PF_INET : PF_INET6, SOCK_STREAM,
  272. !strcmp(proto, "sctp") ? IPPROTO_SCTP : 0)) < 0)
  273. error("socket(): %m");
  274.  
  275. // veth generates link-local IPv6 addresses, so we must bind
  276. // to the interface.
  277. if (ip_version == 6)
  278. {
  279. logit("binding socket to %s", iface);
  280. assert(!setsockopt(sfd, SOL_SOCKET, SO_BINDTODEVICE,
  281. iface, strlen(iface) + 1));
  282. }
  283.  
  284. if (is_client)
  285. return sfd;
  286.  
  287. // bind()
  288. on = 1;
  289. logit("setsockopt(SO_REUSEADDR)");
  290. assert(!setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)));
  291. if (ip_version == 4)
  292. {
  293. struct sockaddr_in saddr;
  294.  
  295. // bind() to @ip:@dst_port.
  296. logit("bind(%s:%u)", ip, dst_port);
  297. fill_saddr4(&saddr, ip, dst_port);
  298. if (bind(sfd, (const struct sockaddr *)&saddr,
  299. sizeof(saddr)) < 0)
  300. error("bind(%s:%u): %m", ip, dst_port);
  301. } else
  302. {
  303. char ip6[IPV6_ADDRLEN];
  304. struct sockaddr_in6 saddr;
  305.  
  306. // Bind to the IPv6 address of @iface and @dst_port.
  307. get_ipv6_address(ip6, iface);
  308. fill_saddr6(sfd, &saddr, iface, ip6, dst_port);
  309.  
  310. logit("bind([%s]:%u)", ip6, dst_port);
  311. if (bind(sfd, (const struct sockaddr *)&saddr,
  312. sizeof(saddr)) < 0)
  313. error("bind([%s]:%u): %m", ip6, dst_port);
  314. }
  315.  
  316. logit("listen()");
  317. assert(!listen(sfd, 1));
  318. return sfd;
  319. }
  320.  
  321. // connect() if necessary and communicate with @dst_ip and @dst_port.
  322. static void __attribute__((noreturn))
  323. run(int is_master, int sfd, unsigned ip_version, const char *proto,
  324. const char *dst_ip, unsigned dst_port, int is_client)
  325. {
  326. const char *iface;
  327.  
  328. iface = is_master ? "veth0" : "veth1";
  329.  
  330. if (!is_client)
  331. { // Server role
  332.  
  333. if (!strcmp(proto, "ping"))
  334. // We'll be pinged, nothing to do anymore.
  335. for (;;)
  336. pause();
  337.  
  338. // TCP or SCTP
  339. for (;;)
  340. {
  341. int cfd;
  342. ssize_t len;
  343. char line[128];
  344.  
  345. // Read the socket line by line and print it.
  346. printf("%6s: accept()...", Whoami);
  347. fflush(stdout);
  348. assert((cfd = accept(sfd, NULL, NULL)) >= 0);
  349. putchar('\n');
  350. while ((len = read(cfd, line, sizeof(line))) > 0)
  351. {
  352. line[len] = '\0';
  353. printf("%s <- %s\n", Whoami, line);
  354. }
  355. close(cfd);
  356. }
  357. } else if (!strcmp(proto, "ping"))
  358. { // ping @dst_ip.
  359. if (ip_version == 4)
  360. cmd("ping", dst_ip, NULL);
  361. else
  362. cmd("ping6", "-I", iface, dst_ip, NULL);
  363. abort();
  364. } else
  365. { // Client role TCP/SCTP
  366. static char const *const alpha[] =
  367. {
  368. "alpha", "beta", "gamma", "delta", "epsilon",
  369. "zeta", "eta", "theta", "iota", "kappa", "lambda",
  370. "mu", "nu", "xi", "omicron", "pi", "rho", "sigma",
  371. "tau", "upsilon", "phi", "chi", "psi", "omega", NULL
  372. };
  373. const char *const *letter;
  374.  
  375. // connect()
  376. if (ip_version == 4)
  377. {
  378. struct sockaddr_in saddr;
  379.  
  380. fill_saddr4(&saddr, dst_ip, dst_port);
  381. printf("%6s: Connecting to %s:%u...",
  382. Whoami, dst_ip, dst_port);
  383. fflush(stdout);
  384. assert(!connect(sfd, (const struct sockaddr *)&saddr,
  385. sizeof(saddr)));
  386. } else
  387. {
  388. struct sockaddr_in6 saddr;
  389.  
  390. fill_saddr6(sfd, &saddr, iface, dst_ip, dst_port);
  391. printf("%6s: Connecting to %s:%u...",
  392. Whoami, dst_ip, dst_port);
  393. fflush(stdout);
  394. assert(!connect(sfd, (const struct sockaddr *)&saddr,
  395. sizeof(saddr)));
  396. }
  397. putchar('\n');
  398.  
  399. // Send @alpha element by element.
  400. letter = &alpha[0];
  401. for (;;)
  402. {
  403. printf("%s -> %s", Whoami, *letter);
  404. assert(write(sfd, *letter, strlen(*letter))
  405. > 0);
  406. sleep(1);
  407. letter++;
  408. if (!*letter)
  409. letter = &alpha[0];
  410. }
  411. }
  412. }
  413.  
  414. /* The main function */
  415. int main(int argc, char *argv[])
  416. {
  417. FILE *st;
  418. char c, line[16];
  419. int optchar, m2s[2], s2m[2];
  420. int opt_master_connects, opt_new_namespace;
  421. unsigned opt_ip_version, opt_server_port;
  422. const char *opt_proto, *opt_master_ip, *opt_slave_ip;
  423.  
  424. // Parse the command line.
  425. opt_ip_version = 4;
  426. opt_proto = "tcp";
  427. opt_master_ip = "1.2.3.4";
  428. opt_slave_ip = "1.2.3.5";
  429. opt_server_port = 2222;
  430. opt_master_connects = 1;
  431. opt_new_namespace = 1;
  432. while ((optchar = getopt(argc, argv, "N46p:m:s:P:i")) != EOF)
  433. switch (optchar)
  434. {
  435. case 'N':
  436. opt_new_namespace = 0;
  437. break;
  438. case '4':
  439. opt_ip_version = 4;
  440. break;
  441. case '6':
  442. opt_ip_version = 6;
  443. break;
  444. case 'p':
  445. opt_proto = optarg;
  446. if (strcmp(opt_proto, "ping")
  447. && strcmp(opt_proto, "tcp")
  448. && strcmp(opt_proto, "sctp"))
  449. usage(argv[0]);
  450. break;
  451. case 'm':
  452. opt_master_ip = optarg;
  453. break;
  454. case 's':
  455. opt_slave_ip = optarg;
  456. break;
  457. case 'P':
  458. opt_server_port = atoi(optarg);
  459. break;
  460. case 'i':
  461. opt_master_connects = !opt_master_connects;
  462. break;
  463. default:
  464. usage(argv[0]);
  465. };
  466.  
  467. setvbuf(stdout, NULL, _IOLBF, 0);
  468. if (geteuid() != 0)
  469. error("you need to be superuser to use this program.");
  470.  
  471. logit("configuring veth0");
  472. cmd("modprobe", "veth", NULL);
  473.  
  474. // Clean up: delete all existing veth devices.
  475. st = cmd_pipe("ip", "link", NULL);
  476. while (fgets(line, sizeof(line), st))
  477. {
  478. char dev[8];
  479. unsigned vethn;
  480.  
  481. if (sscanf(line, "%*u: veth%u:", &vethn) != 1 || vethn % 2)
  482. continue;
  483. snprintf(dev, sizeof(dev), "veth%u", vethn);
  484. cmd("ip", "link", "del", dev, NULL);
  485. }
  486. fclose(st);
  487.  
  488. // Prepare veth0.
  489. cmd("ip", "link", "add", "type", "veth", NULL);
  490. cmd("ip", "addr", "add", opt_master_ip, "dev", "veth0", NULL);
  491. cmd("ifconfig", "veth0", "up", NULL);
  492.  
  493. // Add routes to the destination addresses.
  494. cmd("ip", "route", "add", "to", opt_slave_ip, "dev", "veth0", NULL);
  495. logit("veth0 configured");
  496.  
  497. // Fork to master (outside the namespace) and slave (contained
  498. // in a network namespace) branches.
  499. assert(!pipe(s2m));
  500. assert(!pipe(m2s));
  501. if (!fork())
  502. { // Master
  503. int sfd;
  504. char slave_ip6[IPV6_ADDRLEN];
  505.  
  506. Whoami = "master";
  507. close(m2s[0]);
  508. close(s2m[1]);
  509.  
  510. if (opt_new_namespace)
  511. {
  512. char ppid[8];
  513.  
  514. // Wait until the slave has unshare()d,
  515. // then give it veth1.
  516. logit("waiting for slave to unshare()");
  517. read(s2m[0], &c, 1);
  518. logit("slave has unshared, giving veth1 away");
  519. snprintf(ppid, sizeof(ppid), "%u", getppid());
  520. cmd("ip", "link", "set", "veth1", "netns", ppid,
  521. NULL);
  522. write(m2s[1], "x", 1);
  523. logit("veth1 has been given away");
  524. // We've transferred veth1 to the slave.
  525. }
  526.  
  527. // Wait for slave to configure, then we can acquire
  528. // the veth-generated IPv6 addresses.
  529. logit("waiting for slave to configure veth1");
  530. read(s2m[0], &c, 1);
  531. logit("veth1 configured, preparing socket");
  532. sfd = prepare(1, opt_ip_version, opt_proto,
  533. opt_master_ip, opt_server_port,
  534. opt_master_connects);
  535. logit("prepared socket");
  536.  
  537. if (opt_master_connects)
  538. { // Wait until slave prepares.
  539. if (opt_ip_version == 4)
  540. {
  541. logit("waiting for slave to prepare");
  542. read(s2m[0], &c, 1);
  543. } else
  544. { // Retrieve the slave's IPv6 address.
  545. logit("waiting for slave's IPv6 address");
  546. assert(read(s2m[0], slave_ip6,
  547. sizeof(slave_ip6))
  548. == sizeof(slave_ip6));
  549. }
  550. } else if (opt_ip_version == 6)
  551. { // Send out IPv6 address to the slave.
  552. char master_ip6[IPV6_ADDRLEN];
  553.  
  554. logit("sending our IPv6 address to slave");
  555. get_ipv6_address(master_ip6, "veth0");
  556. write(m2s[1], master_ip6, sizeof(master_ip6));
  557. } else
  558. {
  559. logit("informing slave that we're prepared");
  560. write(m2s[1], "x", 1);
  561. }
  562.  
  563. logit("running...");
  564. run(1, sfd, opt_ip_version, opt_proto,
  565. opt_ip_version == 4 ? opt_slave_ip : slave_ip6,
  566. opt_server_port, opt_master_connects);
  567. } else
  568. { // Slave
  569. int sfd;
  570. char master_ip6[IPV6_ADDRLEN];
  571.  
  572. Whoami = "slave";
  573. close(m2s[1]);
  574. close(s2m[0]);
  575.  
  576. // Let's have our own network space.
  577. if (opt_new_namespace)
  578. {
  579. logit("going into new network space");
  580. assert(!unshare(CLONE_NEWNET));
  581.  
  582. // We've unshare()d.
  583. logit("residing in new network space, waiting for "
  584. "master to give veth1 to us");
  585. write(s2m[1], "x", 1);
  586. read(m2s[0], &c, 1);
  587. // We've got veth1.
  588. }
  589.  
  590. // Configure veth1.
  591. logit("configuring veth1");
  592. cmd("ifconfig", "lo", "up", NULL);
  593. cmd("ifconfig", "veth1", "up", NULL);
  594. cmd("ip", "route", "add", "to", opt_master_ip,
  595. "dev", "veth1", NULL);
  596. cmd("ip", "addr", "add", opt_slave_ip, "dev", "veth1", NULL);
  597. logit("veth1 configured");
  598. write(s2m[1], "x", 1);
  599. // Finished configuration.
  600.  
  601. logit("preparing, socket");
  602. sfd = prepare(0, opt_ip_version, opt_proto,
  603. opt_slave_ip, opt_server_port,
  604. !opt_master_connects);
  605. logit("prepared socket");
  606.  
  607. if (opt_master_connects)
  608. {
  609. if (opt_ip_version == 6)
  610. { // Send our IPv6 address to the master.
  611. char ip6[IPV6_ADDRLEN];
  612.  
  613. logit("sending master our IPv6 address");
  614. get_ipv6_address(ip6, "veth1");
  615. write(s2m[1], ip6, sizeof(ip6));
  616. } else
  617. {
  618. logit("informing master that we're prepared");
  619. write(s2m[1], "x", 1);
  620. }
  621. } else if (opt_ip_version == 6)
  622. {
  623. // Get the master's IPv6 address.
  624. logit("waiting for master's IPv6 address");
  625. assert(read(m2s[0], master_ip6, sizeof(master_ip6))
  626. == sizeof(master_ip6));
  627. } else
  628. {
  629. logit("waiting for master to prepare");
  630. read(m2s[0], &c, 1);
  631. }
  632.  
  633. logit("running...");
  634. run(0, sfd, opt_ip_version, opt_proto,
  635. opt_ip_version == 4 ? opt_master_ip : master_ip6,
  636. opt_server_port, !opt_master_connects);
  637. }
  638.  
  639. /* Not reached */
  640. }
  641.  
  642. /* threesome.c */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement