DF-3296 / fixcheck.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 | /* * DF-3296 fix validation — same setup as trigger_changeaddr but the pool * gets TWO addresses: * - REMOVE nr=0 (not last) -> must succeed (fix must not over-block) * - REMOVE nr=0 (now last) -> must fail EBUSY and must NOT panic * works on patched modules; on stock modules the first REMOVE of the LAST * address is what trigger_changeaddr exercises. */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> #include <net/pf/pfvar.h> static int dev; static void xioc(unsigned long cmd, void *arg, const char *what) { if (ioctl(dev, cmd, arg) == -1) { fprintf(stderr, "%s: %s\n", what, strerror(errno)); exit(2); } } static void add_addr(u_int32_t ticket, const char *ip) { struct pfioc_pooladdr pp; memset(&pp, 0, sizeof(pp)); pp.ticket = ticket; pp.af = AF_INET; pp.addr.addr.type = PF_ADDR_ADDRMASK; pp.addr.addr.v.a.addr.v4.s_addr = inet_addr(ip); pp.addr.addr.v.a.mask.v4.s_addr = inet_addr("255.255.255.255"); xioc(DIOCADDADDR, &pp, "DIOCADDADDR"); } int main(void) { struct pfioc_pooladdr pp, pca; struct pfioc_trans trans; struct pfioc_trans_e ioe; struct pfioc_rule pr; u_int32_t pticket; dev = open("/dev/pf", O_RDWR); if (dev == -1) { perror("open /dev/pf"); return (2); } memset(&pp, 0, sizeof(pp)); xioc(DIOCBEGINADDRS, &pp, "DIOCBEGINADDRS"); pticket = pp.ticket; add_addr(pticket, "10.9.9.9"); add_addr(pticket, "10.9.9.10"); memset(&ioe, 0, sizeof(ioe)); ioe.rs_num = PF_RULESET_NAT; memset(&trans, 0, sizeof(trans)); trans.size = 1; trans.esize = sizeof(ioe); trans.array = &ioe; xioc(DIOCXBEGIN, &trans, "DIOCXBEGIN"); memset(&pr, 0, sizeof(pr)); pr.rule.action = PF_NAT; pr.rule.af = AF_INET; pr.ticket = ioe.ticket; pr.pool_ticket = pticket; xioc(DIOCADDRULE, &pr, "DIOCADDRULE"); xioc(DIOCXCOMMIT, &trans, "DIOCXCOMMIT"); memset(&pr, 0, sizeof(pr)); pr.rule.action = PF_NAT; xioc(DIOCGETRULES, &pr, "DIOCGETRULES"); printf("setup: %u NAT rule(s), ticket %u, pool has 2 addrs\n", pr.nr, pr.ticket); memset(&pca, 0, sizeof(pca)); pca.action = PF_CHANGE_REMOVE; pca.ticket = pr.ticket; pca.nr = 0; pca.r_num = 0; pca.r_action = PF_NAT; pca.af = AF_INET; if (ioctl(dev, DIOCCHANGEADDR, &pca) == -1) printf("remove #1 (not last): FAILED %s (over-blocked?)\n", strerror(errno)); else printf("remove #1 (not last): OK, pool still has 1 addr\n"); if (ioctl(dev, DIOCCHANGEADDR, &pca) == -1) { printf("remove #2 (last): %s (expected EBUSY, no panic)\n", strerror(errno)); return (errno == EBUSY ? 0 : 1); } printf("remove #2 (last): SUCCEEDED (bug present!)\n"); return (1); } |