DF-3558 / echo_neg.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 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | /* * DF echo-pair inertness -- live negative evidence for the pass-2 lead on * sys/netgraph7/echo/ng_echo.c (DF-3540/DF-3553/DF-3557 cycle class). * * Claim under test: a closed echo<->echo DATA cycle is NOT injectable * (echo reflects data back out the arrival hook only -- ng_echo.c:106 * NG_FWD_ITEM_HOOK(item, hook); and its control path RESPONDS to the * original sender -- netgraph.h:998-1013 NG_RESPOND_MSG -> * ng_address_ID(NGI_RETADDR) -- it never re-enters the graph via hooks, * unlike ng_UI's FLOW relay of DF-3557). * * Topology (4 generic messages, DF-3557's dance with echo nodes): * mkpeer : S.a <-> E1.p * mkpeer : E1.q <-> E2.m * rmhook : S.a (E1 keeps q -> survives) * connect : E2.n <-> E1.s ==> closed ring E1.q<->E2.m, E1.s<->E2.n * mkpeer : S.b <-> E1.inj (third hook on E1 for the data-injection * attempt -- must merely bounce back) * * Attempts (all must survive, guest must stay up): * A. DATA injection: sendto(ds, "PING", addr "b") -> reflected straight * back to the socket (readable on ds). Ring never carries traffic. * B. FLOW-cookie control hook-addressed INTO ring hooks ("e1:q", * "e2:n"): echo bounces the message back to the SENDER (response * arrives on cs with NGF_RESP + our token); it does NOT relay into * the ring. * C. Survival sleep. * * Build: cc -O2 -o echo_neg echo_neg.c * Run (root; netgraph.ko + ng_socket.ko + ng_echo.ko loaded): ./echo_neg */ #include <sys/types.h> #include <sys/socket.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #define AF_NG 32 #define NG_CONTROL 2 #define NG_DATA 1 #define NG_VERSION 8 #define GEN_COOKIE 1137070366 /* NGM_GENERIC_COOKIE */ #define FLOW_COOKIE 851672669 /* NGM_FLOW_COOKIE */ #define NGM_MKPEER 2 #define NGM_CONNECT 3 #define NGM_NAME 4 #define NGM_RMHOOK 5 #define NG_TYPESIZ 32 #define NG_HOOKSIZ 32 #define NG_NODESIZ 32 #define HDRSZ 56 struct ngm_hdr_ { u_char version; u_char spare; u_short spare2; u_int arglen, cmd, flags, token, typecookie; char cmdstr[32]; }; struct ng_mesg_ { struct ngm_hdr_ header; char data[2048]; }; struct ngm_mkpeer_ { char type[NG_TYPESIZ]; char ourhook[NG_HOOKSIZ]; char peerhook[NG_HOOKSIZ]; }; struct ngm_name_ { char name[NG_NODESIZ]; }; struct ngm_connect_ { char path[512]; char ourhook[NG_HOOKSIZ]; char peerhook[NG_HOOKSIZ]; }; struct ngm_rmhook_ { char ourhook[NG_HOOKSIZ]; }; struct sockaddr_ng_ { u_char sg_len; u_char sg_family; char sg_data[64]; }; #define NGF_RESP_ 0x00000001u /* ng7 ng_message.h:113 */ static u_int32_t tok = 0xE000; static int cs, ds; static u_int32_t ngc(const char *path, u_int cookie, u_int cmd, const void *arg, u_int arglen) { struct ng_mesg_ m; struct sockaddr_ng_ sa; u_int32_t t = ++tok; ssize_t n; memset(&m, 0, sizeof(m)); m.header.version = NG_VERSION; m.header.arglen = arglen; m.header.cmd = cmd; m.header.token = t; m.header.typecookie = cookie; if (arglen) memcpy(m.data, arg, arglen); memset(&sa, 0, sizeof(sa)); sa.sg_family = AF_NG; snprintf(sa.sg_data, sizeof(sa.sg_data), "%s", path); sa.sg_len = 2 + strlen(sa.sg_data) + 1; n = sendto(cs, &m, HDRSZ + arglen, 0, (struct sockaddr *)&sa, sa.sg_len); if (n < 0) { printf("RESULT sendto(%s) errno=%d (%s)\n", path, errno, strerror(errno)); exit(1); } return (t); } #define STEP(msg) do { printf("STEP %s\n", msg); fflush(stdout); } while (0) int main(void) { struct ngm_mkpeer_ mkp; struct ngm_name_ nm; struct ngm_connect_ con; struct ngm_rmhook_ rmh; struct sockaddr_ng_ sa; struct pollfd pfd; struct ng_mesg_ m; char buf[64]; u_int32_t t; int n, i, resp = 0, bounced = 0; printf("echo_neg: uid=%d\n", getuid()); fflush(stdout); cs = socket(AF_NG, SOCK_DGRAM, NG_CONTROL); if (cs < 0) { printf("RESULT control-socket errno=%d (%s)\n", errno, strerror(errno)); exit(0); } memset(&nm, 0, sizeof(nm)); strcpy(nm.name, "neg"); ngc(".", GEN_COOKIE, NGM_NAME, &nm, sizeof(nm)); STEP("socket node named neg"); /* S.a <-> E1.p */ memset(&mkp, 0, sizeof(mkp)); strcpy(mkp.type, "echo"); strcpy(mkp.ourhook, "a"); strcpy(mkp.peerhook, "p"); ngc(".", GEN_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)); STEP("mkpeer echo(E1) S.a<->E1.p"); memset(&nm, 0, sizeof(nm)); strcpy(nm.name, "e1"); ngc("a", GEN_COOKIE, NGM_NAME, &nm, sizeof(nm)); STEP("named E1 = e1"); /* E1.q <-> E2.m */ memset(&mkp, 0, sizeof(mkp)); strcpy(mkp.type, "echo"); strcpy(mkp.ourhook, "q"); strcpy(mkp.peerhook, "m"); ngc("e1:", GEN_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)); STEP("mkpeer echo(E2) E1.q<->E2.m"); memset(&nm, 0, sizeof(nm)); strcpy(nm.name, "e2"); ngc("a.q", GEN_COOKIE, NGM_NAME, &nm, sizeof(nm)); STEP("named E2 = e2"); /* cut the umbilical: destroy S.a (and E1.p with it) */ memset(&rmh, 0, sizeof(rmh)); strcpy(rmh.ourhook, "a"); ngc("neg:", GEN_COOKIE, NGM_RMHOOK, &rmh, sizeof(rmh)); STEP("rmhook neg:a (E1 survives with q only)"); /* close the ring: E2.n <-> E1.s */ memset(&con, 0, sizeof(con)); strcpy(con.path, "m"); /* from E2: through E2.m -> E1 */ strcpy(con.ourhook, "n"); strcpy(con.peerhook, "s"); ngc("e2:", GEN_COOKIE, NGM_CONNECT, &con, sizeof(con)); STEP("connect E2.n<->E1.s -- RING CLOSED (E1.q<->E2.m, E1.s<->E2.n)"); /* data socket on the ctrl node; third hook for injection attempt */ ds = socket(AF_NG, SOCK_DGRAM, NG_DATA); if (ds < 0) { printf("RESULT data-socket errno=%d (%s)\n", errno, strerror(errno)); exit(1); } memset(&sa, 0, sizeof(sa)); sa.sg_family = AF_NG; strcpy(sa.sg_data, "neg:"); sa.sg_len = 2 + strlen(sa.sg_data) + 1; if (connect(ds, (struct sockaddr *)&sa, sa.sg_len) < 0) { printf("RESULT connect-ds errno=%d\n", errno); exit(1); } STEP("data socket connected to neg"); memset(&mkp, 0, sizeof(mkp)); strcpy(mkp.type, "echo"); strcpy(mkp.ourhook, "b"); strcpy(mkp.peerhook, "inj"); ngc(".", GEN_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)); STEP("mkpeer S.b<->E1.inj (entry-attempt hook)"); /* A. data injection: expect bounce straight back */ memset(&sa, 0, sizeof(sa)); sa.sg_family = AF_NG; strcpy(sa.sg_data, "b"); sa.sg_len = 2 + strlen(sa.sg_data) + 1; if (sendto(ds, "PING", 4, 0, (struct sockaddr *)&sa, sa.sg_len) < 0) { printf("RESULT data-inject errno=%d\n", errno); exit(1); } STEP("A: data sent to E1.inj"); pfd.fd = ds; pfd.events = POLLIN; n = poll(&pfd, 1, 800); if (n > 0) { n = recv(ds, buf, sizeof(buf), MSG_DONTWAIT); if (n > 0) { bounced = 1; printf("STEP A: %d bytes bounced back to sender: " "\"%.*s\" (ring untouched)\n", n, n, buf); } } if (!bounced) printf("STEP A: no bounce observed (n=%d errno=%d)\n", n, errno); fflush(stdout); /* B. FLOW-cookie control hook-addressed into the ring hooks */ for (i = 0; i < 4; i++) { const char *addr = (i & 1) ? "e2:n" : "e1:q"; t = ngc(addr, FLOW_COOKIE, 0, NULL, 0); pfd.fd = cs; pfd.events = POLLIN; n = poll(&pfd, 1, 800); if (n > 0) { n = recv(cs, &m, sizeof(m), MSG_DONTWAIT); if (n >= (int)HDRSZ && m.header.token == t) { resp++; printf("STEP B: %s -> response returned to " "sender: token=%u flags=%#x cookie=%u " "(no relay into ring)\n", addr, m.header.token, m.header.flags, m.header.typecookie); } } else { printf("STEP B: %s -> no response (n=%d)\n", addr, n); } fflush(stdout); } /* C. survive */ sleep(2); printf("RESULT SURVIVED: bounced=%d responses=%d guest-alive\n", bounced, resp); printf("RESULT echo-ring inert: no injection channel (data reflects " "to sender; control responds to sender) -- DF-3557-style FLOW " "relay absent in ng_echo\n"); fflush(stdout); return (0); } |