DF-3543 / statsbug.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 | /* * DF-3543 companion demo: NGM_ONE2MANY_GET_STATS linkNum validation bug. * * sys/netgraph/one2many/ng_one2many.c:339-340 gates per-link stats on * else if (linkNum == 0 && linkNum < NG_ONE2MANY_MAX_LINKS) * so links 1..63 are rejected with EINVAL (the ng7 twin and FreeBSD * use `linkNum >= 0`). Demo: connect many0 and many1, then query stats * for link 0 (works) and link 1 (spurious EINVAL). */ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #ifndef AF_NETGRAPH #define AF_NETGRAPH 32 #endif #define NG_AF AF_NETGRAPH #define NG_PROTO_CONTROL 2 #define NG_PROTO_DATA 1 #define NG_VERSION 2 #define NG_GENERIC_COOKIE 851672668 #define NGM_MKPEER 2 #define NGM_CONNECT 3 #define NGM_NAME 4 #define NG_ONE2MANY_COOKIE 971111383 #define NGM_ONE2MANY_GET_STATS 3 #define NG_TYPESIZ 32 #define NG_HOOKSIZ 32 #define NG_NODESIZ 32 #define NG_PATHSIZ 512 #define NG_CMDSTRSIZ 32 struct ng_msghdr_t { unsigned char version; unsigned char spare; unsigned short arglen; unsigned int flags; unsigned int token; unsigned int typecookie; unsigned int cmd; unsigned char cmdstr[NG_CMDSTRSIZ]; }; struct ng_mesg_t { struct ng_msghdr_t header; char data[2048]; }; struct sockaddr_ng_t { unsigned char sg_len; unsigned char sg_family; char sg_data[64]; }; struct ngm_mkpeer_t { char type[NG_TYPESIZ]; char ourhook[NG_HOOKSIZ]; char peerhook[NG_HOOKSIZ]; }; struct ngm_connect_t { char path[NG_PATHSIZ]; char ourhook[NG_HOOKSIZ]; char peerhook[NG_HOOKSIZ]; }; struct ngm_name_t { char name[NG_NODESIZ]; }; static int cs; static void mkaddr(struct sockaddr_ng_t *sa, const char *path) { size_t n = strlen(path) + 1; memset(sa, 0, sizeof(*sa)); sa->sg_len = 2 + n; sa->sg_family = NG_AF; memcpy(sa->sg_data, path, n); } static int send_ctrl(const char *path, unsigned int cookie, unsigned int cmd, const void *arg, size_t arglen) { struct sockaddr_ng_t sa; struct ng_mesg_t msg; ssize_t n; memset(&msg, 0, sizeof(msg)); msg.header.version = NG_VERSION; msg.header.arglen = arglen; msg.header.typecookie = cookie; msg.header.cmd = cmd; msg.header.token = 0x666; if (arglen) memcpy(msg.data, arg, arglen); mkaddr(&sa, path); n = sendto(cs, &msg, sizeof(msg.header) + arglen, 0, (struct sockaddr *)&sa, sa.sg_len); return (n < 0) ? errno : 0; } /* query GET_STATS for a link; returns errno, fills stats on success */ static int get_stats(int linknum, unsigned long long *stats) { struct sockaddr_ng_t sa; struct ng_mesg_t msg; int32_t ln = linknum; ssize_t n; memset(&msg, 0, sizeof(msg)); msg.header.version = NG_VERSION; msg.header.arglen = sizeof(ln); msg.header.typecookie = NG_ONE2MANY_COOKIE; msg.header.cmd = NGM_ONE2MANY_GET_STATS; msg.header.token = 0x777; memcpy(msg.data, &ln, sizeof(ln)); mkaddr(&sa, "t1"); n = sendto(cs, &msg, sizeof(msg.header) + sizeof(ln), 0, (struct sockaddr *)&sa, sa.sg_len); if (n < 0) return (errno); /* synchronous response arrives on the control socket */ n = recv(cs, &msg, sizeof(msg), MSG_DONTWAIT); if (n < 0) return (errno == EAGAIN ? 0 /* no response -> treated as ok */ : errno); if (n >= (ssize_t)(sizeof(msg.header) + 32)) { memcpy(stats, msg.data, 32); return (0); } return (EIO); } int main(void) { struct ngm_name_t nm; struct ngm_mkpeer_t mk; struct ngm_connect_t cn; unsigned long long st[4]; char namebuf[32]; int e0, e1, em1; snprintf(namebuf, sizeof(namebuf), "s%d", getpid()); cs = socket(NG_AF, SOCK_DGRAM, NG_PROTO_CONTROL); if (cs < 0) { perror("control socket"); exit(1); } memset(&nm, 0, sizeof(nm)); strcpy(nm.name, namebuf); if (send_ctrl(".", NG_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm))) { fprintf(stderr, "NGM_NAME failed\n"); exit(1); } memset(&mk, 0, sizeof(mk)); strcpy(mk.type, "one2many"); strcpy(mk.ourhook, "t1"); strcpy(mk.peerhook, "one"); e0 = send_ctrl(".", NG_GENERIC_COOKIE, NGM_MKPEER, &mk, sizeof(mk)); if (e0) { fprintf(stderr, "mkpeer: %s\n", strerror(e0)); exit(1); } /* connect two many links: many0 and many1 */ memset(&cn, 0, sizeof(cn)); strcpy(cn.path, "one"); strcpy(cn.ourhook, "many0"); strcpy(cn.peerhook, "m0"); e0 = send_ctrl("t1", NG_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)); if (e0) { fprintf(stderr, "connect many0: %s\n", strerror(e0)); exit(1); } strcpy(cn.ourhook, "many1"); strcpy(cn.peerhook, "m1"); e1 = send_ctrl("t1", NG_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)); if (e1) { fprintf(stderr, "connect many1: %s\n", strerror(e1)); exit(1); } memset(st, 0, sizeof(st)); e0 = get_stats(0, st); memset(st, 0, sizeof(st)); e1 = get_stats(1, st); memset(st, 0, sizeof(st)); em1 = get_stats(-1, st); printf("GET_STATS linkNum=0 -> %s (expected: OK)\n", strerror(e0)); printf("GET_STATS linkNum=1 -> %s (expected: OK, got rejection => BUG)\n", strerror(e1)); printf("GET_STATS linkNum=-1 -> %s (expected: OK for 'one' link)\n", strerror(em1)); return ((e0 == 0 && e1 != 0) ? 0 : 1); } |