Index: head/sbin/ipfw/Makefile =================================================================== --- head/sbin/ipfw/Makefile (revision 363163) +++ head/sbin/ipfw/Makefile (revision 363164) @@ -1,19 +1,20 @@ # $FreeBSD$ .include PACKAGE=ipfw PROG= ipfw SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c SRCS+= nat64clat.c nat64lsn.c nat64stl.c nptv6.c -WARNS?= 2 .if ${MK_PF} != "no" SRCS+= altq.c CFLAGS+=-DPF .endif LIBADD= jail util MAN= ipfw.8 .include + +CWARNFLAGS+= -Wno-cast-align Index: head/sbin/ipfw/altq.c =================================================================== --- head/sbin/ipfw/altq.c (revision 363163) +++ head/sbin/ipfw/altq.c (revision 363164) @@ -1,154 +1,154 @@ /*- * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ * * altq interface */ #define PFIOC_USE_LATEST #include #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include /* IFNAMSIZ */ #include #include /* in_addr */ #include /* * Map between current altq queue id numbers and names. */ static TAILQ_HEAD(, pf_altq) altq_entries = TAILQ_HEAD_INITIALIZER(altq_entries); void altq_set_enabled(int enabled) { int pffd; pffd = open("/dev/pf", O_RDWR); if (pffd == -1) err(EX_UNAVAILABLE, "altq support opening pf(4) control device"); if (enabled) { if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST) err(EX_UNAVAILABLE, "enabling altq"); } else { if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT) err(EX_UNAVAILABLE, "disabling altq"); } close(pffd); } static void altq_fetch(void) { struct pfioc_altq pfioc; struct pf_altq *altq; int pffd; unsigned int mnr; static int altq_fetched = 0; if (altq_fetched) return; altq_fetched = 1; pffd = open("/dev/pf", O_RDONLY); if (pffd == -1) { warn("altq support opening pf(4) control device"); return; } bzero(&pfioc, sizeof(pfioc)); pfioc.version = PFIOC_ALTQ_VERSION; if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) { warn("altq support getting queue list"); close(pffd); return; } mnr = pfioc.nr; for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) { if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) { if (errno == EBUSY) break; warn("altq support getting queue list"); close(pffd); return; } if (pfioc.altq.qid == 0) continue; altq = safe_calloc(1, sizeof(*altq)); *altq = pfioc.altq; TAILQ_INSERT_TAIL(&altq_entries, altq, entries); } close(pffd); } u_int32_t altq_name_to_qid(const char *name) { struct pf_altq *altq; altq_fetch(); TAILQ_FOREACH(altq, &altq_entries, entries) if (strcmp(name, altq->qname) == 0) break; if (altq == NULL) errx(EX_DATAERR, "altq has no queue named `%s'", name); return altq->qid; } static const char * altq_qid_to_name(u_int32_t qid) { struct pf_altq *altq; altq_fetch(); TAILQ_FOREACH(altq, &altq_entries, entries) if (qid == altq->qid) break; if (altq == NULL) return NULL; return altq->qname; } void -print_altq_cmd(struct buf_pr *bp, ipfw_insn_altq *altqptr) +print_altq_cmd(struct buf_pr *bp, const ipfw_insn_altq *altqptr) { if (altqptr) { const char *qname; qname = altq_qid_to_name(altqptr->qid); if (qname == NULL) bprintf(bp, " altq ?<%u>", altqptr->qid); else bprintf(bp, " altq %s", qname); } } Index: head/sbin/ipfw/dummynet.c =================================================================== --- head/sbin/ipfw/dummynet.c (revision 363163) +++ head/sbin/ipfw/dummynet.c (revision 363164) @@ -1,1995 +1,1995 @@ /*- * Codel/FQ_Codel and PIE/FQ_PIE Code: * Copyright (C) 2016 Centre for Advanced Internet Architectures, * Swinburne University of Technology, Melbourne, Australia. * Portions of this code were made possible in part by a gift from * The Comcast Innovation Fund. * Implemented by Rasool Al-Saadi * * Copyright (c) 2002-2003,2010 Luigi Rizzo * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * $FreeBSD$ * * dummynet support */ #define NEW_AQM #include #include /* XXX there are several sysctl leftover here */ #include #include "ipfw2.h" #ifdef NEW_AQM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* inet_ntoa */ static struct _s_x dummynet_params[] = { { "plr", TOK_PLR }, { "noerror", TOK_NOERROR }, { "buckets", TOK_BUCKETS }, { "dst-ip", TOK_DSTIP }, { "src-ip", TOK_SRCIP }, { "dst-port", TOK_DSTPORT }, { "src-port", TOK_SRCPORT }, { "proto", TOK_PROTO }, { "weight", TOK_WEIGHT }, { "lmax", TOK_LMAX }, { "maxlen", TOK_LMAX }, { "all", TOK_ALL }, { "mask", TOK_MASK }, /* alias for both */ { "sched_mask", TOK_SCHED_MASK }, { "flow_mask", TOK_FLOW_MASK }, { "droptail", TOK_DROPTAIL }, { "ecn", TOK_ECN }, { "red", TOK_RED }, { "gred", TOK_GRED }, #ifdef NEW_AQM { "codel", TOK_CODEL}, /* Codel AQM */ { "fq_codel", TOK_FQ_CODEL}, /* FQ-Codel */ { "pie", TOK_PIE}, /* PIE AQM */ { "fq_pie", TOK_FQ_PIE}, /* FQ-PIE */ #endif { "bw", TOK_BW }, { "bandwidth", TOK_BW }, { "delay", TOK_DELAY }, { "link", TOK_LINK }, { "pipe", TOK_PIPE }, { "queue", TOK_QUEUE }, { "flowset", TOK_FLOWSET }, { "sched", TOK_SCHED }, { "pri", TOK_PRI }, { "priority", TOK_PRI }, { "type", TOK_TYPE }, { "flow-id", TOK_FLOWID}, { "dst-ipv6", TOK_DSTIP6}, { "dst-ip6", TOK_DSTIP6}, { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, { "profile", TOK_PROFILE}, { "burst", TOK_BURST}, { "dummynet-params", TOK_NULL }, { NULL, 0 } /* terminator */ }; #ifdef NEW_AQM /* AQM/extra sched parameters tokens*/ static struct _s_x aqm_params[] = { { "target", TOK_TARGET}, { "interval", TOK_INTERVAL}, { "limit", TOK_LIMIT}, { "flows", TOK_FLOWS}, { "quantum", TOK_QUANTUM}, { "ecn", TOK_ECN}, { "noecn", TOK_NO_ECN}, { "tupdate", TOK_TUPDATE}, { "max_burst", TOK_MAX_BURST}, { "max_ecnth", TOK_MAX_ECNTH}, { "alpha", TOK_ALPHA}, { "beta", TOK_BETA}, { "capdrop", TOK_CAPDROP}, { "nocapdrop", TOK_NO_CAPDROP}, { "onoff", TOK_ONOFF}, { "dre", TOK_DRE}, { "ts", TOK_TS}, { "derand", TOK_DERAND}, { "noderand", TOK_NO_DERAND}, { NULL, 0 } /* terminator */ }; #endif #define O_NEXT(p, len) ((void *)((char *)p + len)) static void oid_fill(struct dn_id *oid, int len, int type, uintptr_t id) { oid->len = len; oid->type = type; oid->subtype = 0; oid->id = id; } /* make room in the buffer and move the pointer forward */ static void * o_next(struct dn_id **o, int len, int type) { struct dn_id *ret = *o; oid_fill(ret, len, type, 0); *o = O_NEXT(*o, len); return ret; } #ifdef NEW_AQM /* Codel flags */ enum { CODEL_ECN_ENABLED = 1 }; /* PIE flags, from PIE kernel module */ enum { PIE_ECN_ENABLED = 1, PIE_CAPDROP_ENABLED = 2, PIE_ON_OFF_MODE_ENABLED = 4, PIE_DEPRATEEST_ENABLED = 8, PIE_DERAND_ENABLED = 16 }; #define PIE_FIX_POINT_BITS 13 #define PIE_SCALE (1L<15) return -1; for (i = 0; ioid, l, DN_CMD_GET, DN_API_VERSION); ep->oid.len = l; ep->oid.subtype = subtype; ep->nr = nr; ret = do_cmd(-IP_DUMMYNET3, ep, (uintptr_t)&l); if (ret) { free(ep); errx(EX_DATAERR, "Error getting extra parameters\n"); } switch (subtype) { case DN_AQM_PARAMS: if( !strcasecmp(ep->name, "codel")) { us_to_time(ep->par[0], strt1); us_to_time(ep->par[1], strt2); l = sprintf(out, " AQM CoDel target %s interval %s", strt1, strt2); if (ep->par[2] & CODEL_ECN_ENABLED) l = sprintf(out + l, " ECN"); else l += sprintf(out + l, " NoECN"); } else if( !strcasecmp(ep->name, "pie")) { us_to_time(ep->par[0], strt1); us_to_time(ep->par[1], strt2); us_to_time(ep->par[2], strt3); l = sprintf(out, " AQM type PIE target %s tupdate %s alpha " "%g beta %g max_burst %s max_ecnth %.3g", strt1, strt2, ep->par[4] / (float) PIE_SCALE, ep->par[5] / (float) PIE_SCALE, strt3, ep->par[3] / (float) PIE_SCALE ); if (ep->par[6] & PIE_ECN_ENABLED) l += sprintf(out + l, " ECN"); else l += sprintf(out + l, " NoECN"); if (ep->par[6] & PIE_CAPDROP_ENABLED) l += sprintf(out + l, " CapDrop"); else l += sprintf(out + l, " NoCapDrop"); if (ep->par[6] & PIE_ON_OFF_MODE_ENABLED) l += sprintf(out + l, " OnOff"); if (ep->par[6] & PIE_DEPRATEEST_ENABLED) l += sprintf(out + l, " DRE"); else l += sprintf(out + l, " TS"); if (ep->par[6] & PIE_DERAND_ENABLED) l += sprintf(out + l, " Derand"); else l += sprintf(out + l, " NoDerand"); } break; case DN_SCH_PARAMS: if (!strcasecmp(ep->name,"FQ_CODEL")) { us_to_time(ep->par[0], strt1); us_to_time(ep->par[1], strt2); l = sprintf(out," FQ_CODEL target %s interval %s" " quantum %jd limit %jd flows %jd", strt1, strt2, (intmax_t) ep->par[3], (intmax_t) ep->par[4], (intmax_t) ep->par[5] ); if (ep->par[2] & CODEL_ECN_ENABLED) l += sprintf(out + l, " ECN"); else l += sprintf(out + l, " NoECN"); l += sprintf(out + l, "\n"); } else if (!strcasecmp(ep->name,"FQ_PIE")) { us_to_time(ep->par[0], strt1); us_to_time(ep->par[1], strt2); us_to_time(ep->par[2], strt3); l = sprintf(out, " FQ_PIE target %s tupdate %s alpha " "%g beta %g max_burst %s max_ecnth %.3g" " quantum %jd limit %jd flows %jd", strt1, strt2, ep->par[4] / (float) PIE_SCALE, ep->par[5] / (float) PIE_SCALE, strt3, ep->par[3] / (float) PIE_SCALE, (intmax_t) ep->par[7], (intmax_t) ep->par[8], (intmax_t) ep->par[9] ); if (ep->par[6] & PIE_ECN_ENABLED) l += sprintf(out + l, " ECN"); else l += sprintf(out + l, " NoECN"); if (ep->par[6] & PIE_CAPDROP_ENABLED) l += sprintf(out + l, " CapDrop"); else l += sprintf(out + l, " NoCapDrop"); if (ep->par[6] & PIE_ON_OFF_MODE_ENABLED) l += sprintf(out + l, " OnOff"); if (ep->par[6] & PIE_DEPRATEEST_ENABLED) l += sprintf(out + l, " DRE"); else l += sprintf(out + l, " TS"); if (ep->par[6] & PIE_DERAND_ENABLED) l += sprintf(out + l, " Derand"); else l += sprintf(out + l, " NoDerand"); l += sprintf(out + l, "\n"); } break; } free(ep); } #endif #if 0 static int sort_q(void *arg, const void *pa, const void *pb) { int rev = (co.do_sort < 0); int field = rev ? -co.do_sort : co.do_sort; long long res = 0; const struct dn_flow_queue *a = pa; const struct dn_flow_queue *b = pb; switch (field) { case 1: /* pkts */ res = a->len - b->len; break; case 2: /* bytes */ res = a->len_bytes - b->len_bytes; break; case 3: /* tot pkts */ res = a->tot_pkts - b->tot_pkts; break; case 4: /* tot bytes */ res = a->tot_bytes - b->tot_bytes; break; } if (res < 0) res = -1; if (res > 0) res = 1; return (int)(rev ? res : -res); } #endif /* print a mask and header for the subsequent list of flows */ static void print_mask(struct ipfw_flow_id *id) { if (!IS_IP6_FLOW_ID(id)) { printf(" " "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", id->extra ? "queue," : "", id->proto, id->src_ip, id->src_port, id->dst_ip, id->dst_port); } else { char buf[255]; printf("\n mask: %sproto: 0x%02x, flow_id: 0x%08x, ", id->extra ? "queue," : "", id->proto, id->flow_id6); inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf)); printf("%s/0x%04x -> ", buf, id->src_port); inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf)); printf("%s/0x%04x\n", buf, id->dst_port); } } static void print_header(struct ipfw_flow_id *id) { if (!IS_IP6_FLOW_ID(id)) printf("BKT Prot ___Source IP/port____ " "____Dest. IP/port____ " "Tot_pkt/bytes Pkt/Byte Drp\n"); else printf("BKT ___Prot___ _flow-id_ " "______________Source IPv6/port_______________ " "_______________Dest. IPv6/port_______________ " "Tot_pkt/bytes Pkt/Byte Drp\n"); } static void list_flow(struct buf_pr *bp, struct dn_flow *ni) { char buff[255]; struct protoent *pe = NULL; struct in_addr ina; struct ipfw_flow_id *id = &ni->fid; pe = getprotobynumber(id->proto); /* XXX: Should check for IPv4 flows */ bprintf(bp, "%3u%c", (ni->oid.id) & 0xff, id->extra ? '*' : ' '); if (!IS_IP6_FLOW_ID(id)) { if (pe) bprintf(bp, "%-4s ", pe->p_name); else bprintf(bp, "%4u ", id->proto); ina.s_addr = htonl(id->src_ip); bprintf(bp, "%15s/%-5d ", inet_ntoa(ina), id->src_port); ina.s_addr = htonl(id->dst_ip); bprintf(bp, "%15s/%-5d ", inet_ntoa(ina), id->dst_port); } else { /* Print IPv6 flows */ if (pe != NULL) bprintf(bp, "%9s ", pe->p_name); else bprintf(bp, "%9u ", id->proto); bprintf(bp, "%7d %39s/%-5d ", id->flow_id6, inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), id->src_port); bprintf(bp, " %39s/%-5d ", inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), id->dst_port); } pr_u64(bp, &ni->tot_pkts, 4); pr_u64(bp, &ni->tot_bytes, 8); bprintf(bp, "%2u %4u %3u", ni->length, ni->len_bytes, ni->drops); } static void print_flowset_parms(struct dn_fs *fs, char *prefix) { int l; char qs[30]; char plr[30]; char red[200]; /* Display RED parameters */ l = fs->qsize; if (fs->flags & DN_QSIZE_BYTES) { if (l >= 8192) sprintf(qs, "%d KB", l / 1024); else sprintf(qs, "%d B", l); } else sprintf(qs, "%3d sl.", l); if (fs->plr) sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); else plr[0] = '\0'; if (fs->flags & DN_IS_RED) { /* RED parameters */ sprintf(red, "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ', 1.0 * fs->w_q / (double)(1 << SCALE_RED), fs->min_th, fs->max_th, 1.0 * fs->max_p / (double)(1 << SCALE_RED)); if (fs->flags & DN_IS_ECN) strlcat(red, " (ecn)", sizeof(red)); #ifdef NEW_AQM /* get AQM parameters */ } else if (fs->flags & DN_IS_AQM) { get_extra_parms(fs->fs_nr, red, DN_AQM_PARAMS); #endif } else sprintf(red, "droptail"); if (prefix[0]) { printf("%s %s%s %d queues (%d buckets) %s\n", prefix, qs, plr, fs->oid.id, fs->buckets, red); prefix[0] = '\0'; } else { printf("q%05d %s%s %d flows (%d buckets) sched %d " "weight %d lmax %d pri %d %s\n", fs->fs_nr, qs, plr, fs->oid.id, fs->buckets, fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red); if (fs->flags & DN_HAVE_MASK) print_mask(&fs->flow_mask); } } static void print_extra_delay_parms(struct dn_profile *p) { double loss; if (p->samples_no <= 0) return; loss = p->loss_level; loss /= p->samples_no; printf("\t profile: name \"%s\" loss %f samples %d\n", p->name, loss, p->samples_no); } static void flush_buf(char *buf) { if (buf[0]) printf("%s\n", buf); buf[0] = '\0'; } /* * generic list routine. We expect objects in a specific order, i.e. * PIPES AND SCHEDULERS: * link; scheduler; internal flowset if any; instances * we can tell a pipe from the number. * * FLOWSETS: * flowset; queues; * link i (int queue); scheduler i; si(i) { flowsets() : queues } */ static void list_pipes(struct dn_id *oid, struct dn_id *end) { char buf[160]; /* pending buffer */ int toPrint = 1; /* print header */ struct buf_pr bp; buf[0] = '\0'; bp_alloc(&bp, 4096); for (; oid != end; oid = O_NEXT(oid, oid->len)) { if (oid->len < sizeof(*oid)) errx(1, "invalid oid len %d\n", oid->len); switch (oid->type) { default: flush_buf(buf); printf("unrecognized object %d size %d\n", oid->type, oid->len); break; case DN_TEXT: /* list of attached flowsets */ { int i, l; struct { struct dn_id id; uint32_t p[0]; } *d = (void *)oid; l = (oid->len - sizeof(*oid))/sizeof(d->p[0]); if (l == 0) break; printf(" Children flowsets: "); for (i = 0; i < l; i++) printf("%u ", d->p[i]); printf("\n"); break; } case DN_CMD_GET: - if (co.verbose) + if (g_co.verbose) printf("answer for cmd %d, len %d\n", oid->type, oid->id); break; case DN_SCH: { struct dn_sch *s = (struct dn_sch *)oid; flush_buf(buf); printf(" sched %d type %s flags 0x%x %d buckets %d active\n", s->sched_nr, s->name, s->flags, s->buckets, s->oid.id); #ifdef NEW_AQM char parms[200]; get_extra_parms(s->sched_nr, parms, DN_SCH_PARAMS); printf("%s",parms); #endif if (s->flags & DN_HAVE_MASK) print_mask(&s->sched_mask); } break; case DN_FLOW: if (toPrint != 0) { print_header(&((struct dn_flow *)oid)->fid); toPrint = 0; } list_flow(&bp, (struct dn_flow *)oid); printf("%s\n", bp.buf); bp_flush(&bp); break; case DN_LINK: { struct dn_link *p = (struct dn_link *)oid; double b = p->bandwidth; char bwbuf[30]; char burst[5 + 7]; /* This starts a new object so flush buffer */ flush_buf(buf); /* data rate */ if (b == 0) sprintf(bwbuf, "unlimited "); else if (b >= 1000000000) sprintf(bwbuf, "%7.3f Gbit/s", b/1000000000); else if (b >= 1000000) sprintf(bwbuf, "%7.3f Mbit/s", b/1000000); else if (b >= 1000) sprintf(bwbuf, "%7.3f Kbit/s", b/1000); else sprintf(bwbuf, "%7.3f bit/s ", b); if (humanize_number(burst, sizeof(burst), p->burst, - "", HN_AUTOSCALE, 0) < 0 || co.verbose) + "", HN_AUTOSCALE, 0) < 0 || g_co.verbose) sprintf(burst, "%d", (int)p->burst); sprintf(buf, "%05d: %s %4d ms burst %s", p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst); } break; case DN_FS: print_flowset_parms((struct dn_fs *)oid, buf); break; case DN_PROFILE: flush_buf(buf); print_extra_delay_parms((struct dn_profile *)oid); } flush_buf(buf); // XXX does it really go here ? } bp_free(&bp); } /* * Delete pipe, queue or scheduler i */ int ipfw_delete_pipe(int do_pipe, int i) { struct { struct dn_id oid; uintptr_t a[1]; /* add more if we want a list */ } cmd; oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); cmd.oid.subtype = (do_pipe == 1) ? DN_LINK : ( (do_pipe == 2) ? DN_FS : DN_SCH); cmd.a[0] = i; i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len); if (i) { i = 1; warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); } return i; } /* * Code to parse delay profiles. * * Some link types introduce extra delays in the transmission * of a packet, e.g. because of MAC level framing, contention on * the use of the channel, MAC level retransmissions and so on. * From our point of view, the channel is effectively unavailable * for this extra time, which is constant or variable depending * on the link type. Additionally, packets may be dropped after this * time (e.g. on a wireless link after too many retransmissions). * We can model the additional delay with an empirical curve * that represents its distribution. * * cumulative probability * 1.0 ^ * | * L +-- loss-level x * | ****** * | * * | ***** * | * * | ** * | * * +-------*-------------------> * delay * * The empirical curve may have both vertical and horizontal lines. * Vertical lines represent constant delay for a range of * probabilities; horizontal lines correspond to a discontinuty * in the delay distribution: the link will use the largest delay * for a given probability. * * To pass the curve to dummynet, we must store the parameters * in a file as described below, and issue the command * * ipfw pipe config ... bw XXX profile ... * * The file format is the following, with whitespace acting as * a separator and '#' indicating the beginning a comment: * * samples N * the number of samples used in the internal * representation (2..1024; default 100); * * loss-level L * The probability above which packets are lost. * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); * * name identifier * Optional a name (listed by "ipfw pipe show") * to identify the distribution; * * "delay prob" | "prob delay" * One of these two lines is mandatory and defines * the format of the following lines with data points. * * XXX YYY * 2 or more lines representing points in the curve, * with either delay or probability first, according * to the chosen format. * The unit for delay is milliseconds. * * Data points does not need to be ordered or equal to the number * specified in the "samples" line. ipfw will sort and interpolate * the curve as needed. * * Example of a profile file: name bla_bla_bla samples 100 loss-level 0.86 prob delay 0 200 # minimum overhead is 200ms 0.5 200 0.5 300 0.8 1000 0.9 1300 1 1300 * Internally, we will convert the curve to a fixed number of * samples, and when it is time to transmit a packet we will * model the extra delay as extra bits in the packet. * */ #define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN #define ED_TOK_SAMPLES "samples" #define ED_TOK_LOSS "loss-level" #define ED_TOK_NAME "name" #define ED_TOK_DELAY "delay" #define ED_TOK_PROB "prob" #define ED_TOK_BW "bw" #define ED_SEPARATORS " \t\n" #define ED_MIN_SAMPLES_NO 2 /* * returns 1 if s is a non-negative number, with at least one '.' */ static int is_valid_number(const char *s) { int i, dots_found = 0; int len = strlen(s); for (i = 0; i 1)) return 0; return 1; } /* * Take as input a string describing a bandwidth value * and return the numeric bandwidth value. * set clocking interface or bandwidth value */ static void read_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) { if (*bandwidth != -1) warnx("duplicate token, override bandwidth value!"); if (arg[0] >= 'a' && arg[0] <= 'z') { if (!if_name) { errx(1, "no if support"); } if (namelen >= IFNAMSIZ) warn("interface name truncated"); namelen--; /* interface name */ strlcpy(if_name, arg, namelen); *bandwidth = 0; } else { /* read bandwidth value */ int bw; char *end = NULL; bw = strtoul(arg, &end, 0); if (*end == 'K' || *end == 'k') { end++; bw *= 1000; } else if (*end == 'M' || *end == 'm') { end++; bw *= 1000000; } else if (*end == 'G' || *end == 'g') { end++; bw *= 1000000000; } if ((*end == 'B' && _substrcmp2(end, "Bi", "Bit/s") != 0) || _substrcmp2(end, "by", "bytes") == 0) bw *= 8; if (bw < 0) errx(EX_DATAERR, "bandwidth too large"); *bandwidth = bw; if (if_name) if_name[0] = '\0'; } } struct point { double prob; double delay; }; static int compare_points(const void *vp1, const void *vp2) { const struct point *p1 = vp1; const struct point *p2 = vp2; double res = 0; res = p1->prob - p2->prob; if (res == 0) res = p1->delay - p2->delay; if (res < 0) return -1; else if (res > 0) return 1; else return 0; } #define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno static void load_extra_delays(const char *filename, struct dn_profile *p, struct dn_link *link) { char line[ED_MAX_LINE_LEN]; FILE *f; int lineno = 0; int i; int samples = -1; double loss = -1.0; char profile_name[ED_MAX_NAME_LEN]; int delay_first = -1; int do_points = 0; struct point points[ED_MAX_SAMPLES_NO]; int points_no = 0; /* XXX link never NULL? */ p->link_nr = link->link_nr; profile_name[0] = '\0'; f = fopen(filename, "r"); if (f == NULL) err(EX_UNAVAILABLE, "fopen: %s", filename); while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ char *s, *cur = line, *name = NULL, *arg = NULL; ++lineno; /* parse the line */ while (cur) { s = strsep(&cur, ED_SEPARATORS); if (s == NULL || *s == '#') break; if (*s == '\0') continue; if (arg) errx(ED_EFMT("too many arguments")); if (name == NULL) name = s; else arg = s; } if (name == NULL) /* empty line */ continue; if (arg == NULL) errx(ED_EFMT("missing arg for %s"), name); if (!strcasecmp(name, ED_TOK_SAMPLES)) { if (samples > 0) errx(ED_EFMT("duplicate ``samples'' line")); if (atoi(arg) <=0) errx(ED_EFMT("invalid number of samples")); samples = atoi(arg); if (samples>ED_MAX_SAMPLES_NO) errx(ED_EFMT("too many samples, maximum is %d"), ED_MAX_SAMPLES_NO); do_points = 0; } else if (!strcasecmp(name, ED_TOK_BW)) { char buf[IFNAMSIZ]; read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); } else if (!strcasecmp(name, ED_TOK_LOSS)) { if (loss != -1.0) errx(ED_EFMT("duplicated token: %s"), name); if (!is_valid_number(arg)) errx(ED_EFMT("invalid %s"), arg); loss = atof(arg); if (loss > 1) errx(ED_EFMT("%s greater than 1.0"), name); do_points = 0; } else if (!strcasecmp(name, ED_TOK_NAME)) { if (profile_name[0] != '\0') errx(ED_EFMT("duplicated token: %s"), name); strlcpy(profile_name, arg, sizeof(profile_name)); do_points = 0; } else if (!strcasecmp(name, ED_TOK_DELAY)) { if (do_points) errx(ED_EFMT("duplicated token: %s"), name); delay_first = 1; do_points = 1; } else if (!strcasecmp(name, ED_TOK_PROB)) { if (do_points) errx(ED_EFMT("duplicated token: %s"), name); delay_first = 0; do_points = 1; } else if (do_points) { if (!is_valid_number(name) || !is_valid_number(arg)) errx(ED_EFMT("invalid point found")); if (delay_first) { points[points_no].delay = atof(name); points[points_no].prob = atof(arg); } else { points[points_no].delay = atof(arg); points[points_no].prob = atof(name); } if (points[points_no].prob > 1.0) errx(ED_EFMT("probability greater than 1.0")); ++points_no; } else { errx(ED_EFMT("unrecognised command '%s'"), name); } } fclose (f); if (samples == -1) { warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); samples = 100; } if (loss == -1.0) { warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); loss = 1; } /* make sure that there are enough points. */ if (points_no < ED_MIN_SAMPLES_NO) errx(ED_EFMT("too few samples, need at least %d"), ED_MIN_SAMPLES_NO); qsort(points, points_no, sizeof(struct point), compare_points); /* interpolation */ for (i = 0; isamples[ix] = x1; } else { double m = (y2-y1)/(x2-x1); double c = y1 - m*x1; for (; ixsamples[ix] = (ix - c)/m; } } p->samples_no = samples; p->loss_level = loss * samples; strlcpy(p->name, profile_name, sizeof(p->name)); } #ifdef NEW_AQM /* Parse AQM/extra scheduler parameters */ static int process_extra_parms(int *ac, char **av, struct dn_extra_parms *ep, uint16_t type) { int i; /* use kernel defaults */ for (i=0; ipar[i] = -1; switch(type) { case TOK_CODEL: case TOK_FQ_CODEL: /* Codel * 0- target, 1- interval, 2- flags, * FQ_CODEL * 3- quantum, 4- limit, 5- flows */ if (type==TOK_CODEL) ep->par[2] = 0; else ep->par[2] = CODEL_ECN_ENABLED; while (*ac > 0) { int tok = match_token(aqm_params, *av); (*ac)--; av++; switch(tok) { case TOK_TARGET: if (*ac <= 0 || time_to_us(av[0]) < 0) errx(EX_DATAERR, "target needs time\n"); ep->par[0] = time_to_us(av[0]); (*ac)--; av++; break; case TOK_INTERVAL: if (*ac <= 0 || time_to_us(av[0]) < 0) errx(EX_DATAERR, "interval needs time\n"); ep->par[1] = time_to_us(av[0]); (*ac)--; av++; break; case TOK_ECN: ep->par[2] = CODEL_ECN_ENABLED; break; case TOK_NO_ECN: ep->par[2] &= ~CODEL_ECN_ENABLED; break; /* Config fq_codel parameters */ case TOK_QUANTUM: if (type != TOK_FQ_CODEL) errx(EX_DATAERR, "quantum is not for codel\n"); if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "quantum needs number\n"); ep->par[3]= atoi(av[0]); (*ac)--; av++; break; case TOK_LIMIT: if (type != TOK_FQ_CODEL) errx(EX_DATAERR, "limit is not for codel, use queue instead\n"); if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "limit needs number\n"); ep->par[4] = atoi(av[0]); (*ac)--; av++; break; case TOK_FLOWS: if (type != TOK_FQ_CODEL) errx(EX_DATAERR, "flows is not for codel\n"); if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "flows needs number\n"); ep->par[5] = atoi(av[0]); (*ac)--; av++; break; default: printf("%s is Invalid parameter\n", av[-1]); } } break; case TOK_PIE: case TOK_FQ_PIE: /* PIE * 0- target , 1- tupdate, 2- max_burst, * 3- max_ecnth, 4- alpha, * 5- beta, 6- flags * FQ_CODEL * 7- quantum, 8- limit, 9- flows */ if ( type == TOK_PIE) ep->par[6] = PIE_CAPDROP_ENABLED | PIE_DEPRATEEST_ENABLED | PIE_DERAND_ENABLED; else /* for FQ-PIE, use TS mode */ ep->par[6] = PIE_CAPDROP_ENABLED | PIE_DERAND_ENABLED | PIE_ECN_ENABLED; while (*ac > 0) { int tok = match_token(aqm_params, *av); (*ac)--; av++; switch(tok) { case TOK_TARGET: if (*ac <= 0 || time_to_us(av[0]) < 0) errx(EX_DATAERR, "target needs time\n"); ep->par[0] = time_to_us(av[0]); (*ac)--; av++; break; case TOK_TUPDATE: if (*ac <= 0 || time_to_us(av[0]) < 0) errx(EX_DATAERR, "tupdate needs time\n"); ep->par[1] = time_to_us(av[0]); (*ac)--; av++; break; case TOK_MAX_BURST: if (*ac <= 0 || time_to_us(av[0]) < 0) errx(EX_DATAERR, "max_burst needs time\n"); ep->par[2] = time_to_us(av[0]); (*ac)--; av++; break; case TOK_MAX_ECNTH: if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "max_ecnth needs number\n"); ep->par[3] = atof(av[0]) * PIE_SCALE; (*ac)--; av++; break; case TOK_ALPHA: if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "alpha needs number\n"); ep->par[4] = atof(av[0]) * PIE_SCALE; (*ac)--; av++; break; case TOK_BETA: if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "beta needs number\n"); ep->par[5] = atof(av[0]) * PIE_SCALE; (*ac)--; av++; break; case TOK_ECN: ep->par[6] |= PIE_ECN_ENABLED; break; case TOK_NO_ECN: ep->par[6] &= ~PIE_ECN_ENABLED; break; case TOK_CAPDROP: ep->par[6] |= PIE_CAPDROP_ENABLED; break; case TOK_NO_CAPDROP: ep->par[6] &= ~PIE_CAPDROP_ENABLED; break; case TOK_ONOFF: ep->par[6] |= PIE_ON_OFF_MODE_ENABLED; break; case TOK_DRE: ep->par[6] |= PIE_DEPRATEEST_ENABLED; break; case TOK_TS: ep->par[6] &= ~PIE_DEPRATEEST_ENABLED; break; case TOK_DERAND: ep->par[6] |= PIE_DERAND_ENABLED; break; case TOK_NO_DERAND: ep->par[6] &= ~PIE_DERAND_ENABLED; break; /* Config fq_pie parameters */ case TOK_QUANTUM: if (type != TOK_FQ_PIE) errx(EX_DATAERR, "quantum is not for pie\n"); if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "quantum needs number\n"); ep->par[7]= atoi(av[0]); (*ac)--; av++; break; case TOK_LIMIT: if (type != TOK_FQ_PIE) errx(EX_DATAERR, "limit is not for pie, use queue instead\n"); if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "limit needs number\n"); ep->par[8] = atoi(av[0]); (*ac)--; av++; break; case TOK_FLOWS: if (type != TOK_FQ_PIE) errx(EX_DATAERR, "flows is not for pie\n"); if (*ac <= 0 || !is_valid_number(av[0])) errx(EX_DATAERR, "flows needs number\n"); ep->par[9] = atoi(av[0]); (*ac)--; av++; break; default: printf("%s is invalid parameter\n", av[-1]); } } break; } return 0; } #endif /* * configuration of pipes, schedulers, flowsets. * When we configure a new scheduler, an empty pipe is created, so: * * do_pipe = 1 -> "pipe N config ..." only for backward compatibility * sched N+Delta type fifo sched_mask ... * pipe N+Delta * flowset N+Delta pipe N+Delta (no parameters) * sched N type wf2q+ sched_mask ... * pipe N * * do_pipe = 2 -> flowset N config * flowset N parameters * * do_pipe = 3 -> sched N config * sched N parameters (default no pipe) * optional Pipe N config ... * pipe ==> */ void ipfw_config_pipe(int ac, char **av) { int i; u_int j; char *end; struct dn_id *buf, *base; struct dn_sch *sch = NULL; struct dn_link *p = NULL; struct dn_fs *fs = NULL; struct dn_profile *pf = NULL; struct ipfw_flow_id *mask = NULL; #ifdef NEW_AQM struct dn_extra_parms *aqm_extra; struct dn_extra_parms *sch_extra; int lmax_extra; #endif int lmax; uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo; /* * allocate space for 1 header, * 1 scheduler, 1 link, 1 flowset, 1 profile */ lmax = sizeof(struct dn_id); /* command header */ lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + sizeof(struct dn_fs) + sizeof(struct dn_profile); #ifdef NEW_AQM /* Extra Params */ lmax_extra = sizeof(struct dn_extra_parms); /* two lmax_extra because one for AQM params and another * sch params */ lmax += lmax_extra*2; #endif av++; ac--; /* Pipe number */ if (ac && isdigit(**av)) { i = atoi(*av); av++; ac--; } else i = -1; if (i <= 0) errx(EX_USAGE, "need a pipe/flowset/sched number"); base = buf = safe_calloc(1, lmax); /* all commands start with a 'CONFIGURE' and a version */ o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); base->id = DN_API_VERSION; - switch (co.do_pipe) { + switch (g_co.do_pipe) { case 1: /* "pipe N config ..." */ /* Allocate space for the WF2Q+ scheduler, its link * and the FIFO flowset. Set the number, but leave * the scheduler subtype and other parameters to 0 * so the kernel will use appropriate defaults. * XXX todo: add a flag to record if a parameter * is actually configured. * If we do a 'pipe config' mask -> sched_mask. * The FIFO scheduler and link are derived from the * WF2Q+ one in the kernel. */ #ifdef NEW_AQM sch_extra = o_next(&buf, lmax_extra, DN_TEXT); sch_extra ->oid.subtype = 0; /* don't configure scheduler */ #endif sch = o_next(&buf, sizeof(*sch), DN_SCH); p = o_next(&buf, sizeof(*p), DN_LINK); #ifdef NEW_AQM aqm_extra = o_next(&buf, lmax_extra, DN_TEXT); aqm_extra ->oid.subtype = 0; /* don't configure AQM */ #endif fs = o_next(&buf, sizeof(*fs), DN_FS); sch->sched_nr = i; sch->oid.subtype = 0; /* defaults to WF2Q+ */ mask = &sch->sched_mask; flags = &sch->flags; buckets = &sch->buckets; *flags |= DN_PIPE_CMD; p->link_nr = i; /* This flowset is only for the FIFO scheduler */ fs->fs_nr = i + 2*DN_MAX_ID; fs->sched_nr = i + DN_MAX_ID; break; case 2: /* "queue N config ... " */ #ifdef NEW_AQM aqm_extra = o_next(&buf, lmax_extra, DN_TEXT); aqm_extra ->oid.subtype = 0; #endif fs = o_next(&buf, sizeof(*fs), DN_FS); fs->fs_nr = i; mask = &fs->flow_mask; flags = &fs->flags; buckets = &fs->buckets; break; case 3: /* "sched N config ..." */ #ifdef NEW_AQM sch_extra = o_next(&buf, lmax_extra, DN_TEXT); sch_extra ->oid.subtype = 0; #endif sch = o_next(&buf, sizeof(*sch), DN_SCH); #ifdef NEW_AQM aqm_extra = o_next(&buf, lmax_extra, DN_TEXT); aqm_extra ->oid.subtype = 0; #endif fs = o_next(&buf, sizeof(*fs), DN_FS); sch->sched_nr = i; mask = &sch->sched_mask; flags = &sch->flags; buckets = &sch->buckets; /* fs is used only with !MULTIQUEUE schedulers */ fs->fs_nr = i + DN_MAX_ID; fs->sched_nr = i; break; } /* set to -1 those fields for which we want to reuse existing * values from the kernel. * Also, *_nr and subtype = 0 mean reuse the value from the kernel. * XXX todo: support reuse of the mask. */ if (p) p->bandwidth = -1; for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) fs->par[j] = -1; while (ac > 0) { double d; int tok = match_token(dummynet_params, *av); ac--; av++; switch(tok) { case TOK_NOERROR: NEED(fs, "noerror is only for pipes"); fs->flags |= DN_NOERROR; break; case TOK_PLR: NEED(fs, "plr is only for pipes"); NEED1("plr needs argument 0..1\n"); d = strtod(av[0], NULL); if (d > 1) d = 1; else if (d < 0) d = 0; fs->plr = (int)(d*0x7fffffff); ac--; av++; break; case TOK_QUEUE: NEED(fs, "queue is only for pipes or flowsets"); NEED1("queue needs queue size\n"); end = NULL; fs->qsize = strtoul(av[0], &end, 0); if (*end == 'K' || *end == 'k') { fs->flags |= DN_QSIZE_BYTES; fs->qsize *= 1024; } else if (*end == 'B' || _substrcmp2(end, "by", "bytes") == 0) { fs->flags |= DN_QSIZE_BYTES; } ac--; av++; break; case TOK_BUCKETS: NEED(fs, "buckets is only for pipes or flowsets"); NEED1("buckets needs argument\n"); *buckets = strtoul(av[0], NULL, 0); ac--; av++; break; case TOK_FLOW_MASK: case TOK_SCHED_MASK: case TOK_MASK: NEED(mask, "tok_mask"); NEED1("mask needs mask specifier\n"); /* * per-flow queue, mask is dst_ip, dst_port, * src_ip, src_port, proto measured in bits */ bzero(mask, sizeof(*mask)); end = NULL; while (ac >= 1) { uint32_t *p32 = NULL; uint16_t *p16 = NULL; uint32_t *p20 = NULL; struct in6_addr *pa6 = NULL; uint32_t a; tok = match_token(dummynet_params, *av); ac--; av++; switch(tok) { case TOK_ALL: /* * special case, all bits significant * except 'extra' (the queue number) */ mask->dst_ip = ~0; mask->src_ip = ~0; mask->dst_port = ~0; mask->src_port = ~0; mask->proto = ~0; n2mask(&mask->dst_ip6, 128); n2mask(&mask->src_ip6, 128); mask->flow_id6 = ~0; *flags |= DN_HAVE_MASK; goto end_mask; case TOK_QUEUE: mask->extra = ~0; *flags |= DN_HAVE_MASK; goto end_mask; case TOK_DSTIP: mask->addr_type = 4; p32 = &mask->dst_ip; break; case TOK_SRCIP: mask->addr_type = 4; p32 = &mask->src_ip; break; case TOK_DSTIP6: mask->addr_type = 6; pa6 = &mask->dst_ip6; break; case TOK_SRCIP6: mask->addr_type = 6; pa6 = &mask->src_ip6; break; case TOK_FLOWID: mask->addr_type = 6; p20 = &mask->flow_id6; break; case TOK_DSTPORT: p16 = &mask->dst_port; break; case TOK_SRCPORT: p16 = &mask->src_port; break; case TOK_PROTO: break; default: ac++; av--; /* backtrack */ goto end_mask; } if (ac < 1) errx(EX_USAGE, "mask: value missing"); if (*av[0] == '/') { a = strtoul(av[0]+1, &end, 0); if (pa6 == NULL) a = (a == 32) ? ~0 : (1 << a) - 1; } else a = strtoul(av[0], &end, 0); if (p32 != NULL) *p32 = a; else if (p16 != NULL) { if (a > 0xFFFF) errx(EX_DATAERR, "port mask must be 16 bit"); *p16 = (uint16_t)a; } else if (p20 != NULL) { if (a > 0xfffff) errx(EX_DATAERR, "flow_id mask must be 20 bit"); *p20 = (uint32_t)a; } else if (pa6 != NULL) { if (a > 128) errx(EX_DATAERR, "in6addr invalid mask len"); else n2mask(pa6, a); } else { if (a > 0xFF) errx(EX_DATAERR, "proto mask must be 8 bit"); mask->proto = (uint8_t)a; } if (a != 0) *flags |= DN_HAVE_MASK; ac--; av++; } /* end while, config masks */ end_mask: break; #ifdef NEW_AQM case TOK_CODEL: case TOK_PIE: NEED(fs, "codel/pie is only for flowsets"); fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); fs->flags |= DN_IS_AQM; strlcpy(aqm_extra->name, av[-1], sizeof(aqm_extra->name)); aqm_extra->oid.subtype = DN_AQM_PARAMS; process_extra_parms(&ac, av, aqm_extra, tok); break; case TOK_FQ_CODEL: case TOK_FQ_PIE: if (!strcmp(av[-1],"type")) errx(EX_DATAERR, "use type before fq_codel/fq_pie"); NEED(sch, "fq_codel/fq_pie is only for schd"); strlcpy(sch_extra->name, av[-1], sizeof(sch_extra->name)); sch_extra->oid.subtype = DN_SCH_PARAMS; process_extra_parms(&ac, av, sch_extra, tok); break; #endif case TOK_RED: case TOK_GRED: NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); fs->flags |= DN_IS_RED; if (tok == TOK_GRED) fs->flags |= DN_IS_GENTLE_RED; /* * the format for parameters is w_q/min_th/max_th/max_p */ if ((end = strsep(&av[0], "/"))) { double w_q = strtod(end, NULL); if (w_q > 1 || w_q <= 0) errx(EX_DATAERR, "0 < w_q <= 1"); fs->w_q = (int) (w_q * (1 << SCALE_RED)); } if ((end = strsep(&av[0], "/"))) { fs->min_th = strtoul(end, &end, 0); if (*end == 'K' || *end == 'k') fs->min_th *= 1024; } if ((end = strsep(&av[0], "/"))) { fs->max_th = strtoul(end, &end, 0); if (*end == 'K' || *end == 'k') fs->max_th *= 1024; } if ((end = strsep(&av[0], "/"))) { double max_p = strtod(end, NULL); if (max_p > 1 || max_p < 0) errx(EX_DATAERR, "0 <= max_p <= 1"); fs->max_p = (int)(max_p * (1 << SCALE_RED)); } ac--; av++; break; case TOK_ECN: fs->flags |= DN_IS_ECN; break; case TOK_DROPTAIL: NEED(fs, "droptail is only for flowsets"); fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); break; case TOK_BW: NEED(p, "bw is only for links"); NEED1("bw needs bandwidth or interface\n"); read_bandwidth(av[0], &p->bandwidth, NULL, 0); ac--; av++; break; case TOK_DELAY: NEED(p, "delay is only for links"); NEED1("delay needs argument 0..10000ms\n"); p->delay = strtoul(av[0], NULL, 0); ac--; av++; break; case TOK_TYPE: { int l; NEED(sch, "type is only for schedulers"); NEED1("type needs a string"); l = strlen(av[0]); if (l == 0 || l > 15) errx(1, "type %s too long\n", av[0]); strlcpy(sch->name, av[0], sizeof(sch->name)); sch->oid.subtype = 0; /* use string */ #ifdef NEW_AQM /* if fq_codel is selected, consider all tokens after it * as parameters */ if (!strcasecmp(av[0],"fq_codel") || !strcasecmp(av[0],"fq_pie")){ strlcpy(sch_extra->name, av[0], sizeof(sch_extra->name)); sch_extra->oid.subtype = DN_SCH_PARAMS; process_extra_parms(&ac, av, sch_extra, tok); } else { ac--;av++; } #else ac--;av++; #endif break; } case TOK_WEIGHT: NEED(fs, "weight is only for flowsets"); NEED1("weight needs argument\n"); fs->par[0] = strtol(av[0], &end, 0); ac--; av++; break; case TOK_LMAX: NEED(fs, "lmax is only for flowsets"); NEED1("lmax needs argument\n"); fs->par[1] = strtol(av[0], &end, 0); ac--; av++; break; case TOK_PRI: NEED(fs, "priority is only for flowsets"); NEED1("priority needs argument\n"); fs->par[2] = strtol(av[0], &end, 0); ac--; av++; break; case TOK_SCHED: case TOK_PIPE: NEED(fs, "pipe/sched"); NEED1("pipe/link/sched needs number\n"); fs->sched_nr = strtoul(av[0], &end, 0); ac--; av++; break; case TOK_PROFILE: NEED((!pf), "profile already set"); NEED(p, "profile"); { NEED1("extra delay needs the file name\n"); pf = o_next(&buf, sizeof(*pf), DN_PROFILE); load_extra_delays(av[0], pf, p); //XXX can't fail? --ac; ++av; } break; case TOK_BURST: NEED(p, "burst"); NEED1("burst needs argument\n"); errno = 0; if (expand_number(av[0], &p->burst) < 0) if (errno != ERANGE) errx(EX_DATAERR, "burst: invalid argument"); if (errno || p->burst > (1ULL << 48) - 1) errx(EX_DATAERR, "burst: out of range (0..2^48-1)"); ac--; av++; break; default: errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); } } /* check validity of parameters */ if (p) { if (p->delay > 10000) errx(EX_DATAERR, "delay must be < 10000"); if (p->bandwidth == -1) p->bandwidth = 0; } if (fs) { /* XXX accept a 0 scheduler to keep the default */ if (fs->flags & DN_QSIZE_BYTES) { size_t len; long limit; len = sizeof(limit); if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", &limit, &len, NULL, 0) == -1) limit = 1024*1024; if (fs->qsize > limit) errx(EX_DATAERR, "queue size must be < %ldB", limit); } else { size_t len; long limit; len = sizeof(limit); if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", &limit, &len, NULL, 0) == -1) limit = 100; if (fs->qsize > limit) errx(EX_DATAERR, "2 <= queue size <= %ld", limit); } #ifdef NEW_AQM if ((fs->flags & DN_IS_ECN) && !((fs->flags & DN_IS_RED)|| (fs->flags & DN_IS_AQM))) errx(EX_USAGE, "ECN can be used with red/gred/" "codel/fq_codel only!"); #else if ((fs->flags & DN_IS_ECN) && !(fs->flags & DN_IS_RED)) errx(EX_USAGE, "enable red/gred for ECN"); #endif if (fs->flags & DN_IS_RED) { size_t len; int lookup_depth, avg_pkt_size; if (!(fs->flags & DN_IS_ECN) && (fs->min_th >= fs->max_th)) errx(EX_DATAERR, "min_th %d must be < than max_th %d", fs->min_th, fs->max_th); else if ((fs->flags & DN_IS_ECN) && (fs->min_th > fs->max_th)) errx(EX_DATAERR, "min_th %d must be =< than max_th %d", fs->min_th, fs->max_th); if (fs->max_th == 0) errx(EX_DATAERR, "max_th must be > 0"); len = sizeof(int); if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", &lookup_depth, &len, NULL, 0) == -1) lookup_depth = 256; if (lookup_depth == 0) errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" " must be greater than zero"); len = sizeof(int); if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", &avg_pkt_size, &len, NULL, 0) == -1) avg_pkt_size = 512; if (avg_pkt_size == 0) errx(EX_DATAERR, "net.inet.ip.dummynet.red_avg_pkt_size must" " be greater than zero"); #if 0 /* the following computation is now done in the kernel */ /* * Ticks needed for sending a medium-sized packet. * Unfortunately, when we are configuring a WF2Q+ queue, we * do not have bandwidth information, because that is stored * in the parent pipe, and also we have multiple queues * competing for it. So we set s=0, which is not very * correct. But on the other hand, why do we want RED with * WF2Q+ ? */ if (p.bandwidth==0) /* this is a WF2Q+ queue */ s = 0; else s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; /* * max idle time (in ticks) before avg queue size becomes 0. * NOTA: (3/w_q) is approx the value x so that * (1-w_q)^x < 10^-3. */ w_q = ((double)fs->w_q) / (1 << SCALE_RED); idle = s * 3. / w_q; fs->lookup_step = (int)idle / lookup_depth; if (!fs->lookup_step) fs->lookup_step = 1; weight = 1 - w_q; for (t = fs->lookup_step; t > 1; --t) weight *= 1 - w_q; fs->lookup_weight = (int)(weight * (1 << SCALE_RED)); #endif /* code moved in the kernel */ } } i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base); if (i) err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); } void dummynet_flush(void) { struct dn_id oid; oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); do_cmd(IP_DUMMYNET3, &oid, oid.len); } /* Parse input for 'ipfw [pipe|sched|queue] show [range list]' * Returns the number of ranges, and possibly stores them * in the array v of size len. */ static int parse_range(int ac, char *av[], uint32_t *v, int len) { int n = 0; char *endptr, *s; uint32_t base[2]; if (v == NULL || len < 2) { v = base; len = 2; } for (s = *av; s != NULL; av++, ac--) { v[0] = strtoul(s, &endptr, 10); v[1] = (*endptr != '-') ? v[0] : strtoul(endptr+1, &endptr, 10); if (*endptr == '\0') { /* prepare for next round */ s = (ac > 0) ? *(av+1) : NULL; } else { if (*endptr != ',') { warn("invalid number: %s", s); s = ++endptr; continue; } /* continue processing from here */ s = ++endptr; ac++; av--; } if (v[1] < v[0] || v[0] >= DN_MAX_ID-1 || v[1] >= DN_MAX_ID-1) { continue; /* invalid entry */ } n++; /* translate if 'pipe list' */ - if (co.do_pipe == 1) { + if (g_co.do_pipe == 1) { v[0] += DN_MAX_ID; v[1] += DN_MAX_ID; } v = (n*2 < len) ? v + 2 : base; } return n; } /* main entry point for dummynet list functions. co.do_pipe indicates * which function we want to support. * av may contain filtering arguments, either individual entries * or ranges, or lists (space or commas are valid separators). * Format for a range can be n1-n2 or n3 n4 n5 ... * In a range n1 must be <= n2, otherwise the range is ignored. * A number 'n4' is translate in a range 'n4-n4' * All number must be > 0 and < DN_MAX_ID-1 */ void dummynet_list(int ac, char *av[], int show_counters) { struct dn_id *oid, *x = NULL; int ret, i; int n; /* # of ranges */ u_int buflen, l; u_int max_size; /* largest obj passed up */ (void)show_counters; // XXX unused, but we should use it. ac--; av++; /* skip 'list' | 'show' word */ n = parse_range(ac, av, NULL, 0); /* Count # of ranges. */ /* Allocate space to store ranges */ l = sizeof(*oid) + sizeof(uint32_t) * n * 2; oid = safe_calloc(1, l); oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION); if (n > 0) /* store ranges in idx */ parse_range(ac, av, (uint32_t *)(oid + 1), n*2); /* * Compute the size of the largest object returned. If the * response leaves at least this much spare space in the * buffer, then surely the response is complete; otherwise * there might be a risk of truncation and we will need to * retry with a larger buffer. * XXX don't bother with smaller structs. */ max_size = sizeof(struct dn_fs); if (max_size < sizeof(struct dn_sch)) max_size = sizeof(struct dn_sch); if (max_size < sizeof(struct dn_flow)) max_size = sizeof(struct dn_flow); - switch (co.do_pipe) { + switch (g_co.do_pipe) { case 1: oid->subtype = DN_LINK; /* list pipe */ break; case 2: oid->subtype = DN_FS; /* list queue */ break; case 3: oid->subtype = DN_SCH; /* list sched */ break; } /* * Ask the kernel an estimate of the required space (result * in oid.id), unless we are requesting a subset of objects, * in which case the kernel does not give an exact answer. * In any case, space might grow in the meantime due to the * creation of new queues, so we must be prepared to retry. */ if (n > 0) { buflen = 4*1024; } else { ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l); if (ret != 0 || oid->id <= sizeof(*oid)) goto done; buflen = oid->id + max_size; oid->len = sizeof(*oid); /* restore */ } /* Try a few times, until the buffer fits */ for (i = 0; i < 20; i++) { l = buflen; x = safe_realloc(x, l); bcopy(oid, x, oid->len); ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l); if (ret != 0 || x->id <= sizeof(*oid)) goto done; /* no response */ if (l + max_size <= buflen) break; /* ok */ buflen *= 2; /* double for next attempt */ } list_pipes(x, O_NEXT(x, l)); done: if (x) free(x); free(oid); } Index: head/sbin/ipfw/ipfw2.c =================================================================== --- head/sbin/ipfw/ipfw2.c (revision 363163) +++ head/sbin/ipfw/ipfw2.c (revision 363164) @@ -1,5623 +1,5631 @@ /*- * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ */ #include #include #include #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ctime */ #include /* _long_to_time */ #include #include #include /* offsetof */ #include #include /* only IFNAMSIZ */ #include #include /* only n_short, n_long */ #include #include #include #include #include -struct cmdline_opts co; /* global options */ +struct cmdline_opts g_co; /* global options */ struct format_opts { int bcwidth; int pcwidth; int show_counters; int show_time; /* show timestamp */ uint32_t set_mask; /* enabled sets mask */ uint32_t flags; /* request flags */ uint32_t first; /* first rule to request */ uint32_t last; /* last rule to request */ uint32_t dcnt; /* number of dynamic states */ ipfw_obj_ctlv *tstate; /* table state data */ }; int resvd_set_number = RESVD_SET; -int ipfw_socket = -1; +static int ipfw_socket = -1; #define CHECK_LENGTH(v, len) do { \ if ((v) < (len)) \ errx(EX_DATAERR, "Rule too long"); \ } while (0) /* * Check if we have enough space in cmd buffer. Note that since * first 8? u32 words are reserved by reserved header, full cmd * buffer can't be used, so we need to protect from buffer overrun * only. At the beginning, cblen is less than actual buffer size by * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need * for checking small instructions fitting in given range. * We also (ab)use the fact that ipfw_insn is always the first field * for any custom instruction. */ #define CHECK_CMDLEN CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd)) #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ if (!av[0]) \ errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ if (_substrcmp(*av, "tablearg") == 0) { \ arg = IP_FW_TARG; \ break; \ } \ \ { \ long _xval; \ char *end; \ \ _xval = strtol(*av, &end, 10); \ \ if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \ errx(EX_DATAERR, "%s: invalid argument: %s", \ match_value(s_x, tok), *av); \ \ if (errno == ERANGE || _xval < min || _xval > max) \ errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ match_value(s_x, tok), min, max, *av); \ \ if (_xval == IP_FW_TARG) \ errx(EX_DATAERR, "%s: illegal argument value: %s", \ match_value(s_x, tok), *av); \ arg = _xval; \ } \ } while (0) static struct _s_x f_tcpflags[] = { { "syn", TH_SYN }, { "fin", TH_FIN }, { "ack", TH_ACK }, { "psh", TH_PUSH }, { "rst", TH_RST }, { "urg", TH_URG }, { "tcp flag", 0 }, { NULL, 0 } }; static struct _s_x f_tcpopts[] = { { "mss", IP_FW_TCPOPT_MSS }, { "maxseg", IP_FW_TCPOPT_MSS }, { "window", IP_FW_TCPOPT_WINDOW }, { "sack", IP_FW_TCPOPT_SACK }, { "ts", IP_FW_TCPOPT_TS }, { "timestamp", IP_FW_TCPOPT_TS }, { "cc", IP_FW_TCPOPT_CC }, { "tcp option", 0 }, { NULL, 0 } }; /* * IP options span the range 0 to 255 so we need to remap them * (though in fact only the low 5 bits are significant). */ static struct _s_x f_ipopts[] = { { "ssrr", IP_FW_IPOPT_SSRR}, { "lsrr", IP_FW_IPOPT_LSRR}, { "rr", IP_FW_IPOPT_RR}, { "ts", IP_FW_IPOPT_TS}, { "ip option", 0 }, { NULL, 0 } }; static struct _s_x f_iptos[] = { { "lowdelay", IPTOS_LOWDELAY}, { "throughput", IPTOS_THROUGHPUT}, { "reliability", IPTOS_RELIABILITY}, { "mincost", IPTOS_MINCOST}, { "congestion", IPTOS_ECN_CE}, { "ecntransport", IPTOS_ECN_ECT0}, { "ip tos option", 0}, { NULL, 0 } }; struct _s_x f_ipdscp[] = { { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */ { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */ { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */ { "af21", IPTOS_DSCP_AF21 >> 2 }, /* 010010 */ { "af22", IPTOS_DSCP_AF22 >> 2 }, /* 010100 */ { "af23", IPTOS_DSCP_AF23 >> 2 }, /* 010110 */ { "af31", IPTOS_DSCP_AF31 >> 2 }, /* 011010 */ { "af32", IPTOS_DSCP_AF32 >> 2 }, /* 011100 */ { "af33", IPTOS_DSCP_AF33 >> 2 }, /* 011110 */ { "af41", IPTOS_DSCP_AF41 >> 2 }, /* 100010 */ { "af42", IPTOS_DSCP_AF42 >> 2 }, /* 100100 */ { "af43", IPTOS_DSCP_AF43 >> 2 }, /* 100110 */ { "be", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */ { "ef", IPTOS_DSCP_EF >> 2 }, /* 101110 */ { "cs0", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */ { "cs1", IPTOS_DSCP_CS1 >> 2 }, /* 001000 */ { "cs2", IPTOS_DSCP_CS2 >> 2 }, /* 010000 */ { "cs3", IPTOS_DSCP_CS3 >> 2 }, /* 011000 */ { "cs4", IPTOS_DSCP_CS4 >> 2 }, /* 100000 */ { "cs5", IPTOS_DSCP_CS5 >> 2 }, /* 101000 */ { "cs6", IPTOS_DSCP_CS6 >> 2 }, /* 110000 */ { "cs7", IPTOS_DSCP_CS7 >> 2 }, /* 100000 */ { NULL, 0 } }; static struct _s_x limit_masks[] = { {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, {"src-addr", DYN_SRC_ADDR}, {"src-port", DYN_SRC_PORT}, {"dst-addr", DYN_DST_ADDR}, {"dst-port", DYN_DST_PORT}, {NULL, 0} }; /* * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines * This is only used in this code. */ #define IPPROTO_ETHERTYPE 0x1000 static struct _s_x ether_types[] = { /* * Note, we cannot use "-:&/" in the names because they are field * separators in the type specifications. Also, we use s = NULL as * end-delimiter, because a type of 0 can be legal. */ { "ip", 0x0800 }, { "ipv4", 0x0800 }, { "ipv6", 0x86dd }, { "arp", 0x0806 }, { "rarp", 0x8035 }, { "vlan", 0x8100 }, { "loop", 0x9000 }, { "trail", 0x1000 }, { "at", 0x809b }, { "atalk", 0x809b }, { "aarp", 0x80f3 }, { "pppoe_disc", 0x8863 }, { "pppoe_sess", 0x8864 }, { "ipx_8022", 0x00E0 }, { "ipx_8023", 0x0000 }, { "ipx_ii", 0x8137 }, { "ipx_snap", 0x8137 }, { "ipx", 0x8137 }, { "ns", 0x0600 }, { NULL, 0 } }; static struct _s_x rule_eactions[] = { { "nat64clat", TOK_NAT64CLAT }, { "nat64lsn", TOK_NAT64LSN }, { "nat64stl", TOK_NAT64STL }, { "nptv6", TOK_NPTV6 }, { "tcp-setmss", TOK_TCPSETMSS }, { NULL, 0 } /* terminator */ }; static struct _s_x rule_actions[] = { { "abort6", TOK_ABORT6 }, { "abort", TOK_ABORT }, { "accept", TOK_ACCEPT }, { "pass", TOK_ACCEPT }, { "allow", TOK_ACCEPT }, { "permit", TOK_ACCEPT }, { "count", TOK_COUNT }, { "pipe", TOK_PIPE }, { "queue", TOK_QUEUE }, { "divert", TOK_DIVERT }, { "tee", TOK_TEE }, { "netgraph", TOK_NETGRAPH }, { "ngtee", TOK_NGTEE }, { "fwd", TOK_FORWARD }, { "forward", TOK_FORWARD }, { "skipto", TOK_SKIPTO }, { "deny", TOK_DENY }, { "drop", TOK_DENY }, { "reject", TOK_REJECT }, { "reset6", TOK_RESET6 }, { "reset", TOK_RESET }, { "unreach6", TOK_UNREACH6 }, { "unreach", TOK_UNREACH }, { "check-state", TOK_CHECKSTATE }, { "//", TOK_COMMENT }, { "nat", TOK_NAT }, { "reass", TOK_REASS }, { "setfib", TOK_SETFIB }, { "setdscp", TOK_SETDSCP }, { "call", TOK_CALL }, { "return", TOK_RETURN }, { "eaction", TOK_EACTION }, { "tcp-setmss", TOK_TCPSETMSS }, { NULL, 0 } /* terminator */ }; static struct _s_x rule_action_params[] = { { "altq", TOK_ALTQ }, { "log", TOK_LOG }, { "tag", TOK_TAG }, { "untag", TOK_UNTAG }, { NULL, 0 } /* terminator */ }; /* * The 'lookup' instruction accepts one of the following arguments. * -1 is a terminator for the list. * Arguments are passed as v[1] in O_DST_LOOKUP options. */ static int lookup_key[] = { TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, TOK_UID, TOK_JAIL, TOK_DSCP, -1 }; static struct _s_x rule_options[] = { { "tagged", TOK_TAGGED }, { "uid", TOK_UID }, { "gid", TOK_GID }, { "jail", TOK_JAIL }, { "in", TOK_IN }, { "limit", TOK_LIMIT }, { "set-limit", TOK_SETLIMIT }, { "keep-state", TOK_KEEPSTATE }, { "record-state", TOK_RECORDSTATE }, { "bridged", TOK_LAYER2 }, { "layer2", TOK_LAYER2 }, { "out", TOK_OUT }, { "diverted", TOK_DIVERTED }, { "diverted-loopback", TOK_DIVERTEDLOOPBACK }, { "diverted-output", TOK_DIVERTEDOUTPUT }, { "xmit", TOK_XMIT }, { "recv", TOK_RECV }, { "via", TOK_VIA }, { "fragment", TOK_FRAG }, { "frag", TOK_FRAG }, { "fib", TOK_FIB }, { "ipoptions", TOK_IPOPTS }, { "ipopts", TOK_IPOPTS }, { "iplen", TOK_IPLEN }, { "ipid", TOK_IPID }, { "ipprecedence", TOK_IPPRECEDENCE }, { "dscp", TOK_DSCP }, { "iptos", TOK_IPTOS }, { "ipttl", TOK_IPTTL }, { "ipversion", TOK_IPVER }, { "ipver", TOK_IPVER }, { "estab", TOK_ESTAB }, { "established", TOK_ESTAB }, { "setup", TOK_SETUP }, { "sockarg", TOK_SOCKARG }, { "tcpdatalen", TOK_TCPDATALEN }, { "tcpflags", TOK_TCPFLAGS }, { "tcpflgs", TOK_TCPFLAGS }, { "tcpmss", TOK_TCPMSS }, { "tcpoptions", TOK_TCPOPTS }, { "tcpopts", TOK_TCPOPTS }, { "tcpseq", TOK_TCPSEQ }, { "tcpack", TOK_TCPACK }, { "tcpwin", TOK_TCPWIN }, { "icmptype", TOK_ICMPTYPES }, { "icmptypes", TOK_ICMPTYPES }, { "dst-ip", TOK_DSTIP }, { "src-ip", TOK_SRCIP }, { "dst-port", TOK_DSTPORT }, { "src-port", TOK_SRCPORT }, { "proto", TOK_PROTO }, { "MAC", TOK_MAC }, { "mac", TOK_MAC }, { "mac-type", TOK_MACTYPE }, { "verrevpath", TOK_VERREVPATH }, { "versrcreach", TOK_VERSRCREACH }, { "antispoof", TOK_ANTISPOOF }, { "ipsec", TOK_IPSEC }, { "icmp6type", TOK_ICMP6TYPES }, { "icmp6types", TOK_ICMP6TYPES }, { "ext6hdr", TOK_EXT6HDR}, { "flow-id", TOK_FLOWID}, { "ipv6", TOK_IPV6}, { "ip6", TOK_IPV6}, { "ipv4", TOK_IPV4}, { "ip4", TOK_IPV4}, { "dst-ipv6", TOK_DSTIP6}, { "dst-ip6", TOK_DSTIP6}, { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, { "lookup", TOK_LOOKUP}, { "flow", TOK_FLOW}, { "defer-action", TOK_SKIPACTION }, { "defer-immediate-action", TOK_SKIPACTION }, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ { "or", TOK_OR }, /* pseudo option */ { "|", /* escape */ TOK_OR }, /* pseudo option */ { "{", TOK_STARTBRACE }, /* pseudo option */ { "(", TOK_STARTBRACE }, /* pseudo option */ { "}", TOK_ENDBRACE }, /* pseudo option */ { ")", TOK_ENDBRACE }, /* pseudo option */ { NULL, 0 } /* terminator */ }; void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg); static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader **pcfg, size_t *psize); static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av); static void ipfw_list_tifaces(void); struct tidx; -static uint16_t pack_object(struct tidx *tstate, char *name, int otype); -static uint16_t pack_table(struct tidx *tstate, char *name); +static uint16_t pack_object(struct tidx *tstate, const char *name, int otype); +static uint16_t pack_table(struct tidx *tstate, const char *name); static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx); static void object_sort_ctlv(ipfw_obj_ctlv *ctlv); static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type); /* * Simple string buffer API. * Used to simplify buffer passing between function and for * transparent overrun handling. */ /* * Allocates new buffer of given size @sz. * * Returns 0 on success. */ int bp_alloc(struct buf_pr *b, size_t size) { memset(b, 0, sizeof(struct buf_pr)); if ((b->buf = calloc(1, size)) == NULL) return (ENOMEM); b->ptr = b->buf; b->size = size; b->avail = b->size; return (0); } void bp_free(struct buf_pr *b) { free(b->buf); } /* * Flushes buffer so new writer start from beginning. */ void bp_flush(struct buf_pr *b) { b->ptr = b->buf; b->avail = b->size; b->buf[0] = '\0'; } /* * Print message specified by @format and args. * Automatically manage buffer space and transparently handle * buffer overruns. * * Returns number of bytes that should have been printed. */ int -bprintf(struct buf_pr *b, char *format, ...) +bprintf(struct buf_pr *b, const char *format, ...) { va_list args; int i; va_start(args, format); i = vsnprintf(b->ptr, b->avail, format, args); va_end(args); - if (i > b->avail || i < 0) { + if (i < 0 || (size_t)i > b->avail) { /* Overflow or print error */ b->avail = 0; } else { b->ptr += i; b->avail -= i; } b->needed += i; return (i); } /* * Special values printer for tablearg-aware opcodes. */ void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg) { if (str != NULL) bprintf(bp, "%s", str); if (arg == IP_FW_TARG) bprintf(bp, "tablearg"); else bprintf(bp, "%u", arg); } /* * Helper routine to print a possibly unaligned uint64_t on * various platform. If width > 0, print the value with * the desired width, followed by a space; * otherwise, return the required width. */ int pr_u64(struct buf_pr *b, uint64_t *pd, int width) { #ifdef TCC #define U64_FMT "I64" #else #define U64_FMT "llu" #endif uint64_t u; unsigned long long d; bcopy (pd, &u, sizeof(u)); d = u; return (width > 0) ? bprintf(b, "%*" U64_FMT " ", width, d) : snprintf(NULL, 0, "%" U64_FMT, d) ; #undef U64_FMT } void * safe_calloc(size_t number, size_t size) { void *ret = calloc(number, size); if (ret == NULL) err(EX_OSERR, "calloc"); return ret; } void * safe_realloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (ret == NULL) err(EX_OSERR, "realloc"); return ret; } /* * Compare things like interface or table names. */ int stringnum_cmp(const char *a, const char *b) { int la, lb; la = strlen(a); lb = strlen(b); if (la > lb) return (1); else if (la < lb) return (-01); return (strcmp(a, b)); } /* * conditionally runs the command. * Selected options or negative -> getsockopt */ int do_cmd(int optname, void *optval, uintptr_t optlen) { int i; - if (co.test_only) + if (g_co.test_only) return 0; if (ipfw_socket == -1) ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET || optname == IP_FW_ADD || optname == IP_FW3 || optname == IP_FW_NAT_GET_CONFIG || optname < 0 || optname == IP_FW_NAT_GET_LOG) { if (optname < 0) optname = -optname; i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval, (socklen_t *)optlen); } else { i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen); } return i; } /* * do_set3 - pass ipfw control cmd to kernel * @optname: option name * @optval: pointer to option data * @optlen: option length * * Assumes op3 header is already embedded. * Calls setsockopt() with IP_FW3 as kernel-visible opcode. * Returns 0 on success or errno otherwise. */ int do_set3(int optname, ip_fw3_opheader *op3, size_t optlen) { - if (co.test_only) + if (g_co.test_only) return (0); if (ipfw_socket == -1) ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); op3->opcode = optname; return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen)); } /* * do_get3 - pass ipfw control cmd to kernel * @optname: option name * @optval: pointer to option data * @optlen: pointer to option length * * Assumes op3 header is already embedded. * Calls getsockopt() with IP_FW3 as kernel-visible opcode. * Returns 0 on success or errno otherwise. */ int do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) { int error; socklen_t len; - if (co.test_only) + if (g_co.test_only) return (0); if (ipfw_socket == -1) ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); op3->opcode = optname; len = *optlen; error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len); *optlen = len; return (error); } /** * match_token takes a table and a string, returns the value associated * with the string (-1 in case of failure). */ int match_token(struct _s_x *table, const char *string) { struct _s_x *pt; uint i = strlen(string); for (pt = table ; i && pt->s != NULL ; pt++) if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) return pt->x; return (-1); } /** * match_token_relaxed takes a table and a string, returns the value associated * with the string for the best match. * * Returns: * value from @table for matched records * -1 for non-matched records * -2 if more than one records match @string. */ int match_token_relaxed(struct _s_x *table, const char *string) { struct _s_x *pt, *m; int i, c; i = strlen(string); c = 0; for (pt = table ; i != 0 && pt->s != NULL ; pt++) { if (strncmp(pt->s, string, i) != 0) continue; m = pt; c++; } if (c == 1) return (m->x); return (c > 0 ? -2: -1); } int get_token(struct _s_x *table, const char *string, const char *errbase) { int tcmd; if ((tcmd = match_token_relaxed(table, string)) < 0) errx(EX_USAGE, "%s %s %s", (tcmd == 0) ? "invalid" : "ambiguous", errbase, string); return (tcmd); } /** * match_value takes a table and a value, returns the string associated * with the value (NULL in case of failure). */ char const * match_value(struct _s_x *p, int value) { for (; p->s != NULL; p++) if (p->x == value) return p->s; return NULL; } size_t -concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter) +concat_tokens(char *buf, size_t bufsize, struct _s_x *table, + const char *delimiter) { struct _s_x *pt; int l; size_t sz; for (sz = 0, pt = table ; pt->s != NULL; pt++) { l = snprintf(buf + sz, bufsize - sz, "%s%s", (sz == 0) ? "" : delimiter, pt->s); sz += l; bufsize += l; if (sz > bufsize) return (bufsize); } return (sz); } /* * helper function to process a set of flags and set bits in the * appropriate masks. */ int fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set, uint32_t *clear) { char *q; /* points to the separator */ int val; uint32_t *which; /* mask we are working on */ while (p && *p) { if (*p == '!') { p++; which = clear; } else which = set; q = strchr(p, ','); if (q) *q++ = '\0'; val = match_token(flags, p); if (val <= 0) { if (e != NULL) *e = p; return (-1); } *which |= (uint32_t)val; p = q; } return (0); } void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set) { char const *comma = ""; int i, l; for (i = 0; list[i].x != 0; i++) { if ((set & list[i].x) == 0) continue; set &= ~list[i].x; l = snprintf(buf, sz, "%s%s", comma, list[i].s); - if (l >= sz) + if (l < 0 || (size_t)l >= sz) return; comma = ","; buf += l; sz -=l; } } /* * _substrcmp takes two strings and returns 1 if they do not match, * and 0 if they match exactly or the first string is a sub-string * of the second. A warning is printed to stderr in the case that the * first string is a sub-string of the second. * * This function will be removed in the future through the usual * deprecation process. */ int _substrcmp(const char *str1, const char* str2) { if (strncmp(str1, str2, strlen(str1)) != 0) return 1; if (strlen(str1) != strlen(str2)) warnx("DEPRECATED: '%s' matched '%s' as a sub-string", str1, str2); return 0; } /* * _substrcmp2 takes three strings and returns 1 if the first two do not match, * and 0 if they match exactly or the second string is a sub-string * of the first. A warning is printed to stderr in the case that the * first string does not match the third. * * This function exists to warn about the bizarre construction * strncmp(str, "by", 2) which is used to allow people to use a shortcut * for "bytes". The problem is that in addition to accepting "by", * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any * other string beginning with "by". * * This function will be removed in the future through the usual * deprecation process. */ int _substrcmp2(const char *str1, const char* str2, const char* str3) { if (strncmp(str1, str2, strlen(str2)) != 0) return 1; if (strcmp(str1, str3) != 0) warnx("DEPRECATED: '%s' matched '%s'", str1, str3); return 0; } /* * prints one port, symbolic or numeric */ static void print_port(struct buf_pr *bp, int proto, uint16_t port) { if (proto == IPPROTO_ETHERTYPE) { char const *s; - if (co.do_resolv && (s = match_value(ether_types, port)) ) + if (g_co.do_resolv && (s = match_value(ether_types, port)) ) bprintf(bp, "%s", s); else bprintf(bp, "0x%04x", port); } else { struct servent *se = NULL; - if (co.do_resolv) { + if (g_co.do_resolv) { struct protoent *pe = getprotobynumber(proto); se = getservbyport(htons(port), pe ? pe->p_name : NULL); } if (se) bprintf(bp, "%s", se->s_name); else bprintf(bp, "%d", port); } } static struct _s_x _port_name[] = { {"dst-port", O_IP_DSTPORT}, {"src-port", O_IP_SRCPORT}, {"ipid", O_IPID}, {"iplen", O_IPLEN}, {"ipttl", O_IPTTL}, {"mac-type", O_MAC_TYPE}, {"tcpdatalen", O_TCPDATALEN}, {"tcpmss", O_TCPMSS}, {"tcpwin", O_TCPWIN}, {"tagged", O_TAGGED}, {NULL, 0} }; /* * Print the values in a list 16-bit items of the types above. * XXX todo: add support for mask. */ static void -print_newports(struct buf_pr *bp, ipfw_insn_u16 *cmd, int proto, int opcode) +print_newports(struct buf_pr *bp, const ipfw_insn_u16 *cmd, int proto, int opcode) { - uint16_t *p = cmd->ports; + const uint16_t *p = cmd->ports; int i; char const *sep; if (opcode != 0) { sep = match_value(_port_name, opcode); if (sep == NULL) sep = "???"; bprintf(bp, " %s", sep); } sep = " "; - for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { + for (i = F_LEN((const ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { bprintf(bp, "%s", sep); print_port(bp, proto, p[0]); if (p[0] != p[1]) { bprintf(bp, "-"); print_port(bp, proto, p[1]); } sep = ","; } } /* * Like strtol, but also translates service names into port numbers * for some protocols. * In particular: * proto == -1 disables the protocol check; * proto == IPPROTO_ETHERTYPE looks up an internal table * proto == matches the values there. * Returns *end == s in case the parameter is not found. */ static int strtoport(char *s, char **end, int base, int proto) { char *p, *buf; char *s1; int i; *end = s; /* default - not found */ if (*s == '\0') return 0; /* not found */ if (isdigit(*s)) return strtol(s, end, base); /* * find separator. '\\' escapes the next char. */ for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\' || *s1 == '_' || *s1 == '.') ; s1++) if (*s1 == '\\' && s1[1] != '\0') s1++; buf = safe_calloc(s1 - s + 1, 1); /* * copy into a buffer skipping backslashes */ for (p = s, i = 0; p != s1 ; p++) if (*p != '\\') buf[i++] = *p; buf[i++] = '\0'; if (proto == IPPROTO_ETHERTYPE) { i = match_token(ether_types, buf); free(buf); if (i != -1) { /* found */ *end = s1; return i; } } else { struct protoent *pe = NULL; struct servent *se; if (proto != 0) pe = getprotobynumber(proto); setservent(1); se = getservbyname(buf, pe ? pe->p_name : NULL); free(buf); if (se != NULL) { *end = s1; return ntohs(se->s_port); } } return 0; /* not found */ } /* * Fill the body of the command with the list of port ranges. */ static int fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen) { uint16_t a, b, *p = cmd->ports; int i = 0; char *s = av; while (*s) { a = strtoport(av, &s, 0, proto); if (s == av) /* empty or invalid argument */ return (0); CHECK_LENGTH(cblen, i + 2); switch (*s) { case '-': /* a range */ av = s + 1; b = strtoport(av, &s, 0, proto); /* Reject expressions like '1-abc' or '1-2-3'. */ if (s == av || (*s != ',' && *s != '\0')) return (0); p[0] = a; p[1] = b; break; case ',': /* comma separated list */ case '\0': p[0] = p[1] = a; break; default: warnx("port list: invalid separator <%c> in <%s>", *s, av); return (0); } i++; p += 2; av = s + 1; } if (i > 0) { if (i + 1 > F_LEN_MASK) errx(EX_DATAERR, "too many ports/ranges\n"); cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */ } return (i); } /* * Fill the body of the command with the list of DiffServ codepoints. */ static void fill_dscp(ipfw_insn *cmd, char *av, int cblen) { uint32_t *low, *high; char *s = av, *a; int code; cmd->opcode = O_DSCP; cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1; CHECK_CMDLEN; low = (uint32_t *)(cmd + 1); high = low + 1; *low = 0; *high = 0; while (s != NULL) { a = strchr(s, ','); if (a != NULL) *a++ = '\0'; if (isalpha(*s)) { if ((code = match_token(f_ipdscp, s)) == -1) errx(EX_DATAERR, "Unknown DSCP code"); } else { code = strtoul(s, NULL, 10); if (code < 0 || code > 63) errx(EX_DATAERR, "Invalid DSCP value"); } if (code >= 32) *high |= 1 << (code - 32); else *low |= 1 << code; s = a; } } static struct _s_x icmpcodes[] = { { "net", ICMP_UNREACH_NET }, { "host", ICMP_UNREACH_HOST }, { "protocol", ICMP_UNREACH_PROTOCOL }, { "port", ICMP_UNREACH_PORT }, { "needfrag", ICMP_UNREACH_NEEDFRAG }, { "srcfail", ICMP_UNREACH_SRCFAIL }, { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, { "isolated", ICMP_UNREACH_ISOLATED }, { "net-prohib", ICMP_UNREACH_NET_PROHIB }, { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, { "tosnet", ICMP_UNREACH_TOSNET }, { "toshost", ICMP_UNREACH_TOSHOST }, { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, { NULL, 0 } }; static void fill_reject_code(u_short *codep, char *str) { int val; char *s; val = strtoul(str, &s, 0); if (s == str || *s != '\0' || val >= 0x100) val = match_token(icmpcodes, str); if (val < 0) errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); *codep = val; return; } static void print_reject_code(struct buf_pr *bp, uint16_t code) { char const *s; if ((s = match_value(icmpcodes, code)) != NULL) bprintf(bp, "unreach %s", s); else bprintf(bp, "unreach %u", code); } /* * Returns the number of bits set (from left) in a contiguous bitmask, * or -1 if the mask is not contiguous. * XXX this needs a proper fix. * This effectively works on masks in big-endian (network) format. * when compiled on little endian architectures. * * First bit is bit 7 of the first byte -- note, for MAC addresses, * the first bit on the wire is bit 0 of the first byte. * len is the max length in bits. */ int -contigmask(uint8_t *p, int len) +contigmask(const uint8_t *p, int len) { int i, n; for (i=0; iarg1 & 0xff; uint8_t clear = (cmd->arg1 >> 8) & 0xff; if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { bprintf(bp, " setup"); return; } bprintf(bp, " %s ", name); for (i=0; list[i].x != 0; i++) { if (set & list[i].x) { set &= ~list[i].x; bprintf(bp, "%s%s", comma, list[i].s); comma = ","; } if (clear & list[i].x) { clear &= ~list[i].x; bprintf(bp, "%s!%s", comma, list[i].s); comma = ","; } } } /* * Print the ip address contained in a command. */ static void -print_ip(struct buf_pr *bp, const struct format_opts *fo, ipfw_insn_ip *cmd) +print_ip(struct buf_pr *bp, const struct format_opts *fo, + const ipfw_insn_ip *cmd) { struct hostent *he = NULL; - struct in_addr *ia; - uint32_t len = F_LEN((ipfw_insn *)cmd); - uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; + const struct in_addr *ia; + const uint32_t *a = ((const ipfw_insn_u32 *)cmd)->d; + uint32_t len = F_LEN((const ipfw_insn *)cmd); char *t; bprintf(bp, " "); if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { uint32_t d = a[1]; const char *arg = ""; if (d < sizeof(lookup_key)/sizeof(lookup_key[0])) arg = match_value(rule_options, lookup_key[d]); - t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); + t = table_search_ctlv(fo->tstate, + ((const ipfw_insn *)cmd)->arg1); bprintf(bp, "lookup %s %s", arg, t); return; } if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { bprintf(bp, "me"); return; } if (cmd->o.opcode == O_IP_SRC_LOOKUP || cmd->o.opcode == O_IP_DST_LOOKUP) { - t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); + t = table_search_ctlv(fo->tstate, + ((const ipfw_insn *)cmd)->arg1); bprintf(bp, "table(%s", t); if (len == F_INSN_SIZE(ipfw_insn_u32)) bprintf(bp, ",%u", *a); bprintf(bp, ")"); return; } if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { - uint32_t x, *map = (uint32_t *)&(cmd->mask); + const uint32_t *map = (const uint32_t *)&cmd->mask; + struct in_addr addr; + uint32_t x; int i, j; char comma = '{'; x = cmd->o.arg1 - 1; - x = htonl( ~x ); - cmd->addr.s_addr = htonl(cmd->addr.s_addr); - bprintf(bp, "%s/%d", inet_ntoa(cmd->addr), - contigmask((uint8_t *)&x, 32)); - x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); + x = htonl(~x); + addr.s_addr = htonl(cmd->addr.s_addr); + bprintf(bp, "%s/%d", inet_ntoa(addr), + contigmask((uint8_t *)&x, 32)); + x = cmd->addr.s_addr; x &= 0xff; /* base */ /* * Print bits and ranges. * Locate first bit set (i), then locate first bit unset (j). * If we have 3+ consecutive bits set, then print them as a * range, otherwise only print the initial bit and rescan. */ for (i=0; i < cmd->o.arg1; i++) if (map[i/32] & (1<<(i & 31))) { for (j=i+1; j < cmd->o.arg1; j++) if (!(map[ j/32] & (1<<(j & 31)))) break; bprintf(bp, "%c%d", comma, i+x); if (j>i+2) { /* range has at least 3 elements */ bprintf(bp, "-%d", j-1+x); i = j-1; } comma = ','; } bprintf(bp, "}"); return; } /* * len == 2 indicates a single IP, whereas lists of 1 or more * addr/mask pairs have len = (2n+1). We convert len to n so we * use that to count the number of entries. */ for (len = len / 2; len > 0; len--, a += 2) { int mb = /* mask length */ (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ? - 32 : contigmask((uint8_t *)&(a[1]), 32); - if (mb == 32 && co.do_resolv) - he = gethostbyaddr((char *)&(a[0]), sizeof(in_addr_t), + 32 : contigmask((const uint8_t *)&(a[1]), 32); + if (mb == 32 && g_co.do_resolv) + he = gethostbyaddr((const char *)&(a[0]), sizeof(in_addr_t), AF_INET); if (he != NULL) /* resolved to name */ bprintf(bp, "%s", he->h_name); else if (mb == 0) /* any */ bprintf(bp, "any"); else { /* numeric IP followed by some kind of mask */ - ia = (struct in_addr *)&a[0]; + ia = (const struct in_addr *)&a[0]; bprintf(bp, "%s", inet_ntoa(*ia)); if (mb < 0) { - ia = (struct in_addr *)&a[1]; + ia = (const struct in_addr *)&a[1]; bprintf(bp, ":%s", inet_ntoa(*ia)); } else if (mb < 32) bprintf(bp, "/%d", mb); } if (len > 1) bprintf(bp, ","); } } /* * prints a MAC address/mask pair */ static void -format_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask) +format_mac(struct buf_pr *bp, const uint8_t *addr, const uint8_t *mask) { int l = contigmask(mask, 48); if (l == 0) bprintf(bp, " any"); else { bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); if (l == -1) bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x", mask[0], mask[1], mask[2], mask[3], mask[4], mask[5]); else if (l < 48) bprintf(bp, "/%d", l); } } static void -print_mac(struct buf_pr *bp, ipfw_insn_mac *mac) +print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac) { bprintf(bp, " MAC"); format_mac(bp, mac->addr, mac->mask); format_mac(bp, mac->addr + 6, mac->mask + 6); } static void fill_icmptypes(ipfw_insn_u32 *cmd, char *av) { uint8_t type; cmd->d[0] = 0; while (*av) { if (*av == ',') av++; type = strtoul(av, &av, 0); if (*av != ',' && *av != '\0') errx(EX_DATAERR, "invalid ICMP type"); if (type > 31) errx(EX_DATAERR, "ICMP type out of range"); cmd->d[0] |= 1 << type; } cmd->o.opcode = O_ICMPTYPE; cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); } static void -print_icmptypes(struct buf_pr *bp, ipfw_insn_u32 *cmd) +print_icmptypes(struct buf_pr *bp, const ipfw_insn_u32 *cmd) { int i; char sep= ' '; bprintf(bp, " icmptypes"); for (i = 0; i < 32; i++) { if ( (cmd->d[0] & (1 << (i))) == 0) continue; bprintf(bp, "%c%d", sep, i); sep = ','; } } static void -print_dscp(struct buf_pr *bp, ipfw_insn_u32 *cmd) +print_dscp(struct buf_pr *bp, const ipfw_insn_u32 *cmd) { + const uint32_t *v; + const char *code; int i = 0; - uint32_t *v; char sep= ' '; - const char *code; bprintf(bp, " dscp"); v = cmd->d; while (i < 64) { if (*v & (1 << i)) { if ((code = match_value(f_ipdscp, i)) != NULL) bprintf(bp, "%c%s", sep, code); else bprintf(bp, "%c%d", sep, i); sep = ','; } if ((++i % 32) == 0) v++; } } -#define insntod(cmd, type) ((ipfw_insn_ ## type *)(cmd)) +#define insntod(cmd, type) ((const ipfw_insn_ ## type *)(cmd)) struct show_state { struct ip_fw_rule *rule; const ipfw_insn *eaction; uint8_t *printed; int flags; #define HAVE_PROTO 0x0001 #define HAVE_SRCIP 0x0002 #define HAVE_DSTIP 0x0004 #define HAVE_PROBE_STATE 0x0008 int proto; int or_block; }; static int init_show_state(struct show_state *state, struct ip_fw_rule *rule) { state->printed = calloc(rule->cmd_len, sizeof(uint8_t)); if (state->printed == NULL) return (ENOMEM); state->rule = rule; state->eaction = NULL; state->flags = 0; state->proto = 0; state->or_block = 0; return (0); } static void free_show_state(struct show_state *state) { free(state->printed); } static uint8_t is_printed_opcode(struct show_state *state, const ipfw_insn *cmd) { return (state->printed[cmd - state->rule->cmd]); } static void mark_printed(struct show_state *state, const ipfw_insn *cmd) { state->printed[cmd - state->rule->cmd] = 1; } static void print_limit_mask(struct buf_pr *bp, const ipfw_insn_limit *limit) { struct _s_x *p = limit_masks; char const *comma = " "; uint8_t x; for (x = limit->limit_mask; p->x != 0; p++) { if ((x & p->x) == p->x) { x &= ~p->x; bprintf(bp, "%s%s", comma, p->s); comma = ","; } } bprint_uint_arg(bp, " ", limit->conn_limit); } static int print_instruction(struct buf_pr *bp, const struct format_opts *fo, - struct show_state *state, ipfw_insn *cmd) + struct show_state *state, const ipfw_insn *cmd) { struct protoent *pe; struct passwd *pwd; struct group *grp; const char *s; double d; if (is_printed_opcode(state, cmd)) return (0); if ((cmd->len & F_OR) != 0 && state->or_block == 0) bprintf(bp, " {"); if (cmd->opcode != O_IN && (cmd->len & F_NOT) != 0) bprintf(bp, " not"); switch (cmd->opcode) { case O_PROB: d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff; bprintf(bp, "prob %f ", d); break; case O_PROBE_STATE: /* no need to print anything here */ state->flags |= HAVE_PROBE_STATE; break; case O_IP_SRC: case O_IP_SRC_LOOKUP: case O_IP_SRC_MASK: case O_IP_SRC_ME: case O_IP_SRC_SET: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip"); print_ip(bp, fo, insntod(cmd, ip)); break; case O_IP_DST: case O_IP_DST_LOOKUP: case O_IP_DST_MASK: case O_IP_DST_ME: case O_IP_DST_SET: if (state->flags & HAVE_DSTIP) bprintf(bp, " dst-ip"); print_ip(bp, fo, insntod(cmd, ip)); break; case O_IP6_SRC: case O_IP6_SRC_MASK: case O_IP6_SRC_ME: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip6"); print_ip6(bp, insntod(cmd, ip6)); break; case O_IP6_DST: case O_IP6_DST_MASK: case O_IP6_DST_ME: if (state->flags & HAVE_DSTIP) bprintf(bp, " dst-ip6"); print_ip6(bp, insntod(cmd, ip6)); break; case O_FLOW6ID: print_flow6id(bp, insntod(cmd, u32)); break; case O_IP_DSTPORT: case O_IP_SRCPORT: print_newports(bp, insntod(cmd, u16), state->proto, (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) == (HAVE_SRCIP | HAVE_DSTIP) ? cmd->opcode: 0); break; case O_PROTO: pe = getprotobynumber(cmd->arg1); if (state->flags & HAVE_PROTO) bprintf(bp, " proto"); if (pe != NULL) bprintf(bp, " %s", pe->p_name); else bprintf(bp, " %u", cmd->arg1); state->proto = cmd->arg1; break; case O_MACADDR2: print_mac(bp, insntod(cmd, mac)); break; case O_MAC_TYPE: print_newports(bp, insntod(cmd, u16), IPPROTO_ETHERTYPE, cmd->opcode); break; case O_FRAG: bprintf(bp, " frag"); break; case O_FIB: bprintf(bp, " fib %u", cmd->arg1); break; case O_SOCKARG: bprintf(bp, " sockarg"); break; case O_IN: bprintf(bp, cmd->len & F_NOT ? " out" : " in"); break; case O_DIVERTED: switch (cmd->arg1) { case 3: bprintf(bp, " diverted"); break; case 2: bprintf(bp, " diverted-output"); break; case 1: bprintf(bp, " diverted-loopback"); break; default: bprintf(bp, " diverted-?<%u>", cmd->arg1); break; } break; case O_LAYER2: bprintf(bp, " layer2"); break; case O_XMIT: case O_RECV: case O_VIA: if (cmd->opcode == O_XMIT) s = "xmit"; else if (cmd->opcode == O_RECV) s = "recv"; else /* if (cmd->opcode == O_VIA) */ s = "via"; switch (insntod(cmd, if)->name[0]) { case '\0': bprintf(bp, " %s %s", s, inet_ntoa(insntod(cmd, if)->p.ip)); break; case '\1': bprintf(bp, " %s table(%s)", s, table_search_ctlv(fo->tstate, insntod(cmd, if)->p.kidx)); break; default: bprintf(bp, " %s %s", s, insntod(cmd, if)->name); } break; case O_IP_FLOW_LOOKUP: s = table_search_ctlv(fo->tstate, cmd->arg1); bprintf(bp, " flow table(%s", s); if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) bprintf(bp, ",%u", insntod(cmd, u32)->d[0]); bprintf(bp, ")"); break; case O_IPID: case O_IPTTL: case O_IPLEN: case O_TCPDATALEN: case O_TCPMSS: case O_TCPWIN: if (F_LEN(cmd) == 1) { switch (cmd->opcode) { case O_IPID: s = "ipid"; break; case O_IPTTL: s = "ipttl"; break; case O_IPLEN: s = "iplen"; break; case O_TCPDATALEN: s = "tcpdatalen"; break; case O_TCPMSS: s = "tcpmss"; break; case O_TCPWIN: s = "tcpwin"; break; } bprintf(bp, " %s %u", s, cmd->arg1); } else print_newports(bp, insntod(cmd, u16), 0, cmd->opcode); break; case O_IPVER: bprintf(bp, " ipver %u", cmd->arg1); break; case O_IPPRECEDENCE: bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5); break; case O_DSCP: print_dscp(bp, insntod(cmd, u32)); break; case O_IPOPT: print_flags(bp, "ipoptions", cmd, f_ipopts); break; case O_IPTOS: print_flags(bp, "iptos", cmd, f_iptos); break; case O_ICMPTYPE: print_icmptypes(bp, insntod(cmd, u32)); break; case O_ESTAB: bprintf(bp, " established"); break; case O_TCPFLAGS: print_flags(bp, "tcpflags", cmd, f_tcpflags); break; case O_TCPOPTS: print_flags(bp, "tcpoptions", cmd, f_tcpopts); break; case O_TCPACK: bprintf(bp, " tcpack %d", ntohl(insntod(cmd, u32)->d[0])); break; case O_TCPSEQ: bprintf(bp, " tcpseq %d", ntohl(insntod(cmd, u32)->d[0])); break; case O_UID: pwd = getpwuid(insntod(cmd, u32)->d[0]); if (pwd != NULL) bprintf(bp, " uid %s", pwd->pw_name); else bprintf(bp, " uid %u", insntod(cmd, u32)->d[0]); break; case O_GID: grp = getgrgid(insntod(cmd, u32)->d[0]); if (grp != NULL) bprintf(bp, " gid %s", grp->gr_name); else bprintf(bp, " gid %u", insntod(cmd, u32)->d[0]); break; case O_JAIL: bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]); break; case O_VERREVPATH: bprintf(bp, " verrevpath"); break; case O_VERSRCREACH: bprintf(bp, " versrcreach"); break; case O_ANTISPOOF: bprintf(bp, " antispoof"); break; case O_IPSEC: bprintf(bp, " ipsec"); break; case O_NOP: - bprintf(bp, " // %s", (char *)(cmd + 1)); + bprintf(bp, " // %s", (const char *)(cmd + 1)); break; case O_KEEP_STATE: if (state->flags & HAVE_PROBE_STATE) bprintf(bp, " keep-state"); else bprintf(bp, " record-state"); bprintf(bp, " :%s", object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_STATE_NAME)); break; case O_LIMIT: if (state->flags & HAVE_PROBE_STATE) bprintf(bp, " limit"); else bprintf(bp, " set-limit"); print_limit_mask(bp, insntod(cmd, limit)); bprintf(bp, " :%s", object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_STATE_NAME)); break; case O_IP6: if (state->flags & HAVE_PROTO) bprintf(bp, " proto"); bprintf(bp, " ip6"); break; case O_IP4: if (state->flags & HAVE_PROTO) bprintf(bp, " proto"); bprintf(bp, " ip4"); break; case O_ICMP6TYPE: print_icmp6types(bp, insntod(cmd, u32)); break; case O_EXT_HDR: print_ext6hdr(bp, cmd); break; case O_TAGGED: if (F_LEN(cmd) == 1) bprint_uint_arg(bp, " tagged ", cmd->arg1); else print_newports(bp, insntod(cmd, u16), 0, O_TAGGED); break; case O_SKIP_ACTION: bprintf(bp, " defer-immediate-action"); break; default: bprintf(bp, " [opcode %d len %d]", cmd->opcode, cmd->len); } if (cmd->len & F_OR) { bprintf(bp, " or"); state->or_block = 1; } else if (state->or_block != 0) { bprintf(bp, " }"); state->or_block = 0; } mark_printed(state, cmd); return (1); } static ipfw_insn * print_opcode(struct buf_pr *bp, struct format_opts *fo, struct show_state *state, int opcode) { ipfw_insn *cmd; int l; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { /* We use zero opcode to print the rest of options */ if (opcode >= 0 && cmd->opcode != opcode) continue; /* * Skip O_NOP, when we printing the rest * of options, it will be handled separately. */ if (cmd->opcode == O_NOP && opcode != O_NOP) continue; if (!print_instruction(bp, fo, state, cmd)) continue; return (cmd); } return (NULL); } static void print_fwd(struct buf_pr *bp, const ipfw_insn *cmd) { char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2]; - ipfw_insn_sa6 *sa6; - ipfw_insn_sa *sa; + const ipfw_insn_sa6 *sa6; + const ipfw_insn_sa *sa; uint16_t port; if (cmd->opcode == O_FORWARD_IP) { sa = insntod(cmd, sa); port = sa->sa.sin_port; if (sa->sa.sin_addr.s_addr == INADDR_ANY) bprintf(bp, "fwd tablearg"); else bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr)); } else { sa6 = insntod(cmd, sa6); port = sa6->sa.sin6_port; bprintf(bp, "fwd "); if (getnameinfo((const struct sockaddr *)&sa6->sa, sizeof(struct sockaddr_in6), buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) == 0) bprintf(bp, "%s", buf); } if (port != 0) bprintf(bp, ",%u", port); } static int print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, struct show_state *state, const ipfw_insn *cmd) { const char *s; if (is_printed_opcode(state, cmd)) return (0); switch (cmd->opcode) { case O_CHECK_STATE: bprintf(bp, "check-state"); if (cmd->arg1 != 0) s = object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_STATE_NAME); else s = NULL; bprintf(bp, " :%s", s ? s: "any"); break; case O_ACCEPT: bprintf(bp, "allow"); break; case O_COUNT: bprintf(bp, "count"); break; case O_DENY: bprintf(bp, "deny"); break; case O_REJECT: if (cmd->arg1 == ICMP_REJECT_RST) bprintf(bp, "reset"); else if (cmd->arg1 == ICMP_REJECT_ABORT) bprintf(bp, "abort"); else if (cmd->arg1 == ICMP_UNREACH_HOST) bprintf(bp, "reject"); else print_reject_code(bp, cmd->arg1); break; case O_UNREACH6: if (cmd->arg1 == ICMP6_UNREACH_RST) bprintf(bp, "reset6"); else if (cmd->arg1 == ICMP6_UNREACH_ABORT) bprintf(bp, "abort6"); else print_unreach6_code(bp, cmd->arg1); break; case O_SKIPTO: bprint_uint_arg(bp, "skipto ", cmd->arg1); break; case O_PIPE: bprint_uint_arg(bp, "pipe ", cmd->arg1); break; case O_QUEUE: bprint_uint_arg(bp, "queue ", cmd->arg1); break; case O_DIVERT: bprint_uint_arg(bp, "divert ", cmd->arg1); break; case O_TEE: bprint_uint_arg(bp, "tee ", cmd->arg1); break; case O_NETGRAPH: bprint_uint_arg(bp, "netgraph ", cmd->arg1); break; case O_NGTEE: bprint_uint_arg(bp, "ngtee ", cmd->arg1); break; case O_FORWARD_IP: case O_FORWARD_IP6: print_fwd(bp, cmd); break; case O_LOG: if (insntod(cmd, log)->max_log > 0) bprintf(bp, " log logamount %d", insntod(cmd, log)->max_log); else bprintf(bp, " log"); break; case O_ALTQ: #ifndef NO_ALTQ print_altq_cmd(bp, insntod(cmd, altq)); #endif break; case O_TAG: bprint_uint_arg(bp, cmd->len & F_NOT ? " untag ": " tag ", cmd->arg1); break; case O_NAT: if (cmd->arg1 != IP_FW_NAT44_GLOBAL) bprint_uint_arg(bp, "nat ", cmd->arg1); else bprintf(bp, "nat global"); break; case O_SETFIB: if (cmd->arg1 == IP_FW_TARG) bprint_uint_arg(bp, "setfib ", cmd->arg1); else bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF); break; case O_EXTERNAL_ACTION: /* * The external action can consists of two following * each other opcodes - O_EXTERNAL_ACTION and * O_EXTERNAL_INSTANCE. The first contains the ID of * name of external action. The second contains the ID * of name of external action instance. * NOTE: in case when external action has no named * instances support, the second opcode isn't needed. */ state->eaction = cmd; s = object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_EACTION); if (match_token(rule_eactions, s) != -1) bprintf(bp, "%s", s); else bprintf(bp, "eaction %s", s); break; case O_EXTERNAL_INSTANCE: if (state->eaction == NULL) break; /* * XXX: we need to teach ipfw(9) to rewrite opcodes * in the user buffer on rule addition. When we add * the rule, we specify zero TLV type for * O_EXTERNAL_INSTANCE object. To show correct * rule after `ipfw add` we need to search instance * name with zero type. But when we do `ipfw show` * we calculate TLV type using IPFW_TLV_EACTION_NAME() * macro. */ s = object_search_ctlv(fo->tstate, cmd->arg1, 0); if (s == NULL) s = object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_EACTION_NAME( state->eaction->arg1)); bprintf(bp, " %s", s); break; case O_EXTERNAL_DATA: if (state->eaction == NULL) break; /* * Currently we support data formatting only for * external data with datalen u16. For unknown data * print its size in bytes. */ if (cmd->len == F_INSN_SIZE(ipfw_insn)) bprintf(bp, " %u", cmd->arg1); else bprintf(bp, " %ubytes", cmd->len * sizeof(uint32_t)); break; case O_SETDSCP: if (cmd->arg1 == IP_FW_TARG) { bprintf(bp, "setdscp tablearg"); break; } s = match_value(f_ipdscp, cmd->arg1 & 0x3F); if (s != NULL) bprintf(bp, "setdscp %s", s); else bprintf(bp, "setdscp %u", cmd->arg1 & 0x3F); break; case O_REASS: bprintf(bp, "reass"); break; case O_CALLRETURN: if (cmd->len & F_NOT) bprintf(bp, "return"); else bprint_uint_arg(bp, "call ", cmd->arg1); break; default: bprintf(bp, "** unrecognized action %d len %d ", cmd->opcode, cmd->len); } mark_printed(state, cmd); return (1); } static ipfw_insn * print_action(struct buf_pr *bp, struct format_opts *fo, struct show_state *state, uint8_t opcode) { ipfw_insn *cmd; int l; for (l = state->rule->cmd_len - state->rule->act_ofs, cmd = ACTION_PTR(state->rule); l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (cmd->opcode != opcode) continue; if (!print_action_instruction(bp, fo, state, cmd)) continue; return (cmd); } return (NULL); } static void print_proto(struct buf_pr *bp, struct format_opts *fo, struct show_state *state) { ipfw_insn *cmd; int l, proto, ip4, ip6; /* Count all O_PROTO, O_IP4, O_IP6 instructions. */ proto = ip4 = ip6 = 0; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { switch (cmd->opcode) { case O_PROTO: proto++; break; case O_IP4: ip4 = 1; if (cmd->len & F_OR) ip4++; break; case O_IP6: ip6 = 1; if (cmd->len & F_OR) ip6++; break; default: continue; } } if (proto == 0 && ip4 == 0 && ip6 == 0) { state->proto = IPPROTO_IP; state->flags |= HAVE_PROTO; bprintf(bp, " ip"); return; } /* To handle the case { ip4 or ip6 }, print opcode with F_OR first */ cmd = NULL; if (ip4 || ip6) cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP4: O_IP6); if (cmd != NULL && (cmd->len & F_OR)) cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP6: O_IP4); if (cmd == NULL || (cmd->len & F_OR)) for (l = proto; l > 0; l--) { cmd = print_opcode(bp, fo, state, O_PROTO); if (cmd == NULL || (cmd->len & F_OR) == 0) break; } /* Initialize proto, it is used by print_newports() */ state->flags |= HAVE_PROTO; if (state->proto == 0 && ip6 != 0) state->proto = IPPROTO_IPV6; } static int match_opcode(int opcode, const int opcodes[], size_t nops) { - int i; + size_t i; for (i = 0; i < nops; i++) if (opcode == opcodes[i]) return (1); return (0); } static void print_address(struct buf_pr *bp, struct format_opts *fo, struct show_state *state, const int opcodes[], size_t nops, int portop, int flag) { ipfw_insn *cmd; int count, l, portcnt, pf; count = portcnt = 0; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (match_opcode(cmd->opcode, opcodes, nops)) count++; else if (cmd->opcode == portop) portcnt++; } if (count == 0) bprintf(bp, " any"); for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0 && count > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (!match_opcode(cmd->opcode, opcodes, nops)) continue; print_instruction(bp, fo, state, cmd); if ((cmd->len & F_OR) == 0) break; count--; } /* * If several O_IP_?PORT opcodes specified, leave them to the * options section. */ if (portcnt == 1) { for (l = state->rule->act_ofs, cmd = state->rule->cmd, pf = 0; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (cmd->opcode != portop) { pf = (cmd->len & F_OR); continue; } /* Print opcode iff it is not in OR block. */ if (pf == 0 && (cmd->len & F_OR) == 0) print_instruction(bp, fo, state, cmd); break; } } state->flags |= flag; } static const int action_opcodes[] = { O_CHECK_STATE, O_ACCEPT, O_COUNT, O_DENY, O_REJECT, O_UNREACH6, O_SKIPTO, O_PIPE, O_QUEUE, O_DIVERT, O_TEE, O_NETGRAPH, O_NGTEE, O_FORWARD_IP, O_FORWARD_IP6, O_NAT, O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN, /* keep the following opcodes at the end of the list */ O_EXTERNAL_ACTION, O_EXTERNAL_INSTANCE, O_EXTERNAL_DATA }; static const int modifier_opcodes[] = { O_LOG, O_ALTQ, O_TAG }; static const int src_opcodes[] = { O_IP_SRC, O_IP_SRC_LOOKUP, O_IP_SRC_MASK, O_IP_SRC_ME, O_IP_SRC_SET, O_IP6_SRC, O_IP6_SRC_MASK, O_IP6_SRC_ME }; static const int dst_opcodes[] = { O_IP_DST, O_IP_DST_LOOKUP, O_IP_DST_MASK, O_IP_DST_ME, O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME }; static void show_static_rule(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) { + static int twidth = 0; struct show_state state; ipfw_insn *cmd; - static int twidth = 0; - int i; + size_t i; /* Print # DISABLED or skip the rule */ if ((fo->set_mask & (1 << rule->set)) == 0) { /* disabled mask */ if (!co->show_sets) return; else bprintf(bp, "# DISABLED "); } if (init_show_state(&state, rule) != 0) { warn("init_show_state() failed"); return; } bprintf(bp, "%05u ", rule->rulenum); /* Print counters if enabled */ if (fo->pcwidth > 0 || fo->bcwidth > 0) { pr_u64(bp, &cntr->pcnt, fo->pcwidth); pr_u64(bp, &cntr->bcnt, fo->bcwidth); } /* Print timestamp */ if (co->do_time == TIMESTAMP_NUMERIC) bprintf(bp, "%10u ", cntr->timestamp); else if (co->do_time == TIMESTAMP_STRING) { char timestr[30]; time_t t = (time_t)0; if (twidth == 0) { strcpy(timestr, ctime(&t)); *strchr(timestr, '\n') = '\0'; twidth = strlen(timestr); } if (cntr->timestamp > 0) { t = _long_to_time(cntr->timestamp); strcpy(timestr, ctime(&t)); *strchr(timestr, '\n') = '\0'; bprintf(bp, "%s ", timestr); } else { bprintf(bp, "%*s", twidth, " "); } } /* Print set number */ if (co->show_sets) bprintf(bp, "set %d ", rule->set); /* Print the optional "match probability" */ cmd = print_opcode(bp, fo, &state, O_PROB); /* Print rule action */ for (i = 0; i < nitems(action_opcodes); i++) { cmd = print_action(bp, fo, &state, action_opcodes[i]); if (cmd == NULL) continue; /* Handle special cases */ switch (cmd->opcode) { case O_CHECK_STATE: goto end; case O_EXTERNAL_ACTION: case O_EXTERNAL_INSTANCE: /* External action can have several instructions */ continue; } break; } /* Print rule modifiers */ for (i = 0; i < nitems(modifier_opcodes); i++) print_action(bp, fo, &state, modifier_opcodes[i]); /* * Print rule body */ if (co->comment_only != 0) goto end; if (rule->flags & IPFW_RULE_JUSTOPTS) { state.flags |= HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP; goto justopts; } print_proto(bp, fo, &state); if (co->do_compact != 0 && (rule->flags & IPFW_RULE_NOOPT)) goto justopts; /* Print source */ bprintf(bp, " from"); print_address(bp, fo, &state, src_opcodes, nitems(src_opcodes), O_IP_SRCPORT, HAVE_SRCIP); /* Print destination */ bprintf(bp, " to"); print_address(bp, fo, &state, dst_opcodes, nitems(dst_opcodes), O_IP_DSTPORT, HAVE_DSTIP); justopts: /* Print the rest of options */ while (print_opcode(bp, fo, &state, -1)) ; end: /* Print comment at the end */ cmd = print_opcode(bp, fo, &state, O_NOP); if (co->comment_only != 0 && cmd == NULL) bprintf(bp, " // ..."); bprintf(bp, "\n"); free_show_state(&state); } static void show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, ipfw_dyn_rule *d) { struct protoent *pe; struct in_addr a; uint16_t rulenum; char buf[INET6_ADDRSTRLEN]; if (d->expire == 0 && d->dyn_type != O_LIMIT_PARENT) return; bcopy(&d->rule, &rulenum, sizeof(rulenum)); bprintf(bp, "%05d", rulenum); if (fo->pcwidth > 0 || fo->bcwidth > 0) { bprintf(bp, " "); pr_u64(bp, &d->pcnt, fo->pcwidth); pr_u64(bp, &d->bcnt, fo->bcwidth); bprintf(bp, "(%ds)", d->expire); } switch (d->dyn_type) { case O_LIMIT_PARENT: bprintf(bp, " PARENT %d", d->count); break; case O_LIMIT: bprintf(bp, " LIMIT"); break; case O_KEEP_STATE: /* bidir, no mask */ bprintf(bp, " STATE"); break; } if ((pe = getprotobynumber(d->id.proto)) != NULL) bprintf(bp, " %s", pe->p_name); else bprintf(bp, " proto %u", d->id.proto); if (d->id.addr_type == 4) { a.s_addr = htonl(d->id.src_ip); bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port); a.s_addr = htonl(d->id.dst_ip); bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port); } else if (d->id.addr_type == 6) { bprintf(bp, " %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, sizeof(buf)), d->id.src_port); bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, sizeof(buf)), d->id.dst_port); } else bprintf(bp, " UNKNOWN <-> UNKNOWN"); if (d->kidx != 0) bprintf(bp, " :%s", object_search_ctlv(fo->tstate, d->kidx, IPFW_TLV_STATE_NAME)); #define BOTH_SYN (TH_SYN | (TH_SYN << 8)) #define BOTH_FIN (TH_FIN | (TH_FIN << 8)) if (co->verbose) { bprintf(bp, " state 0x%08x%s", d->state, d->state ? " ": ","); if (d->state & IPFW_DYN_ORPHANED) bprintf(bp, "ORPHANED,"); if ((d->state & BOTH_SYN) == BOTH_SYN) bprintf(bp, "BOTH_SYN,"); else { if (d->state & TH_SYN) bprintf(bp, "F_SYN,"); if (d->state & (TH_SYN << 8)) bprintf(bp, "R_SYN,"); } if ((d->state & BOTH_FIN) == BOTH_FIN) bprintf(bp, "BOTH_FIN,"); else { if (d->state & TH_FIN) bprintf(bp, "F_FIN,"); if (d->state & (TH_FIN << 8)) bprintf(bp, "R_FIN,"); } bprintf(bp, " f_ack 0x%x, r_ack 0x%x", d->ack_fwd, d->ack_rev); } } static int do_range_cmd(int cmd, ipfw_range_tlv *rt) { ipfw_range_header rh; size_t sz; memset(&rh, 0, sizeof(rh)); memcpy(&rh.range, rt, sizeof(*rt)); rh.range.head.length = sizeof(*rt); rh.range.head.type = IPFW_TLV_RANGE; sz = sizeof(rh); if (do_get3(cmd, &rh.opheader, &sz) != 0) return (-1); /* Save number of matched objects */ rt->new_set = rh.range.new_set; return (0); } /* * This one handles all set-related commands * ipfw set { show | enable | disable } * ipfw set swap X Y * ipfw set move X to Y * ipfw set move rule X to Y */ void ipfw_sets_handler(char *av[]) { ipfw_range_tlv rt; - char *msg; + const char *msg; size_t size; uint32_t masks[2]; int i; uint16_t rulenum; uint8_t cmd; av++; memset(&rt, 0, sizeof(rt)); if (av[0] == NULL) errx(EX_USAGE, "set needs command"); if (_substrcmp(*av, "show") == 0) { struct format_opts fo; ipfw_cfg_lheader *cfg; memset(&fo, 0, sizeof(fo)); - if (ipfw_get_config(&co, &fo, &cfg, &size) != 0) + if (ipfw_get_config(&g_co, &fo, &cfg, &size) != 0) err(EX_OSERR, "requesting config failed"); for (i = 0, msg = "disable"; i < RESVD_SET; i++) if ((cfg->set_mask & (1<set_mask != (uint32_t)-1) ? " enable" : "enable"; for (i = 0; i < RESVD_SET; i++) if ((cfg->set_mask & (1< RESVD_SET) errx(EX_DATAERR, "invalid set number %s\n", av[0]); if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET) errx(EX_DATAERR, "invalid set number %s\n", av[1]); i = do_range_cmd(IP_FW_SET_SWAP, &rt); } else if (_substrcmp(*av, "move") == 0) { av++; if (av[0] && _substrcmp(*av, "rule") == 0) { rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */ cmd = IP_FW_XMOVE; av++; } else cmd = IP_FW_SET_MOVE; /* Move set to new one */ if (av[0] == NULL || av[1] == NULL || av[2] == NULL || av[3] != NULL || _substrcmp(av[1], "to") != 0) errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); rulenum = atoi(av[0]); rt.new_set = atoi(av[2]); if (cmd == IP_FW_XMOVE) { rt.start_rule = rulenum; rt.end_rule = rulenum; } else rt.set = rulenum; rt.new_set = atoi(av[2]); if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) || (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) ) errx(EX_DATAERR, "invalid source number %s\n", av[0]); if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET) errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); i = do_range_cmd(cmd, &rt); if (i < 0) err(EX_OSERR, "failed to move %s", cmd == IP_FW_SET_MOVE ? "set": "rule"); } else if (_substrcmp(*av, "disable") == 0 || _substrcmp(*av, "enable") == 0 ) { int which = _substrcmp(*av, "enable") == 0 ? 1 : 0; av++; masks[0] = masks[1] = 0; while (av[0]) { if (isdigit(**av)) { i = atoi(*av); if (i < 0 || i > RESVD_SET) errx(EX_DATAERR, "invalid set number %d\n", i); masks[which] |= (1<dcnt++; if (fo->show_counters == 0) return; if (co->use_set) { /* skip states from another set */ bcopy((char *)&d->rule + sizeof(uint16_t), &set, sizeof(uint8_t)); if (set != co->use_set - 1) return; } width = pr_u64(NULL, &d->pcnt, 0); if (width > fo->pcwidth) fo->pcwidth = width; width = pr_u64(NULL, &d->bcnt, 0); if (width > fo->bcwidth) fo->bcwidth = width; } static int foreach_state(struct cmdline_opts *co, struct format_opts *fo, caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg) { int ttype; state_cb *fptr; void *farg; ipfw_obj_tlv *tlv; ipfw_obj_ctlv *ctlv; fptr = NULL; ttype = 0; while (sz > 0) { ctlv = (ipfw_obj_ctlv *)base; switch (ctlv->head.type) { case IPFW_TLV_DYNSTATE_LIST: base += sizeof(*ctlv); sz -= sizeof(*ctlv); ttype = IPFW_TLV_DYN_ENT; fptr = dyn_bc; farg = dyn_arg; break; default: return (sz); } while (sz > 0) { tlv = (ipfw_obj_tlv *)base; if (tlv->type != ttype) break; fptr(co, fo, farg, tlv + 1); sz -= tlv->length; base += tlv->length; } } return (sz); } static void prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz) { int bcwidth, pcwidth, width; int n; struct ip_fw_bcounter *cntr; struct ip_fw_rule *r; bcwidth = 0; pcwidth = 0; if (fo->show_counters != 0) { for (n = 0; n < rcnt; n++, rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { cntr = (struct ip_fw_bcounter *)(rtlv + 1); r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); /* skip rules from another set */ if (co->use_set && r->set != co->use_set - 1) continue; /* packet counter */ width = pr_u64(NULL, &cntr->pcnt, 0); if (width > pcwidth) pcwidth = width; /* byte counter */ width = pr_u64(NULL, &cntr->bcnt, 0); if (width > bcwidth) bcwidth = width; } } fo->bcwidth = bcwidth; fo->pcwidth = pcwidth; fo->dcnt = 0; if (co->do_dynamic && dynsz > 0) foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL); } static int list_static_range(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt) { int n, seen; struct ip_fw_rule *r; struct ip_fw_bcounter *cntr; int c = 0; for (n = seen = 0; n < rcnt; n++, rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { if ((fo->show_counters | fo->show_time) != 0) { cntr = (struct ip_fw_bcounter *)(rtlv + 1); r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); } else { cntr = NULL; r = (struct ip_fw_rule *)(rtlv + 1); } if (r->rulenum > fo->last) break; if (co->use_set && r->set != co->use_set - 1) continue; if (r->rulenum >= fo->first && r->rulenum <= fo->last) { show_static_rule(co, fo, bp, r, cntr); printf("%s", bp->buf); c += rtlv->length; bp_flush(bp); seen++; } } return (seen); } static void list_dyn_state(struct cmdline_opts *co, struct format_opts *fo, void *_arg, void *_state) { uint16_t rulenum; uint8_t set; ipfw_dyn_rule *d; struct buf_pr *bp; d = (ipfw_dyn_rule *)_state; bp = (struct buf_pr *)_arg; bcopy(&d->rule, &rulenum, sizeof(rulenum)); if (rulenum > fo->last) return; if (co->use_set) { bcopy((char *)&d->rule + sizeof(uint16_t), &set, sizeof(uint8_t)); if (set != co->use_set - 1) return; } if (rulenum >= fo->first) { show_dyn_state(co, fo, bp, d); printf("%s\n", bp->buf); bp_flush(bp); } } static int list_dyn_range(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, caddr_t base, size_t sz) { sz = foreach_state(co, fo, base, sz, list_dyn_state, bp); return (sz); } void ipfw_list(int ac, char *av[], int show_counters) { ipfw_cfg_lheader *cfg; struct format_opts sfo; size_t sz; int error; int lac; char **lav; uint32_t rnum; char *endptr; - if (co.test_only) { + if (g_co.test_only) { fprintf(stderr, "Testing only, list disabled\n"); return; } - if (co.do_pipe) { + if (g_co.do_pipe) { dummynet_list(ac, av, show_counters); return; } ac--; av++; memset(&sfo, 0, sizeof(sfo)); /* Determine rule range to request */ if (ac > 0) { for (lac = ac, lav = av; lac != 0; lac--) { rnum = strtoul(*lav++, &endptr, 10); if (sfo.first == 0 || rnum < sfo.first) sfo.first = rnum; if (*endptr == '-') rnum = strtoul(endptr + 1, &endptr, 10); if (sfo.last == 0 || rnum > sfo.last) sfo.last = rnum; } } /* get configuraion from kernel */ cfg = NULL; sfo.show_counters = show_counters; - sfo.show_time = co.do_time; - if (co.do_dynamic != 2) + sfo.show_time = g_co.do_time; + if (g_co.do_dynamic != 2) sfo.flags |= IPFW_CFG_GET_STATIC; - if (co.do_dynamic != 0) + if (g_co.do_dynamic != 0) sfo.flags |= IPFW_CFG_GET_STATES; if ((sfo.show_counters | sfo.show_time) != 0) sfo.flags |= IPFW_CFG_GET_COUNTERS; - if (ipfw_get_config(&co, &sfo, &cfg, &sz) != 0) + if (ipfw_get_config(&g_co, &sfo, &cfg, &sz) != 0) err(EX_OSERR, "retrieving config failed"); - error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av); + error = ipfw_show_config(&g_co, &sfo, cfg, sz, ac, av); free(cfg); if (error != EX_OK) exit(error); } static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[]) { caddr_t dynbase; size_t dynsz; int rcnt; int exitval = EX_OK; int lac; char **lav; char *endptr; size_t readsz; struct buf_pr bp; ipfw_obj_ctlv *ctlv, *tstate; ipfw_obj_tlv *rbase; /* * Handle tablenames TLV first, if any */ tstate = NULL; rbase = NULL; dynbase = NULL; dynsz = 0; readsz = sizeof(*cfg); rcnt = 0; fo->set_mask = cfg->set_mask; ctlv = (ipfw_obj_ctlv *)(cfg + 1); if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { object_sort_ctlv(ctlv); fo->tstate = ctlv; readsz += ctlv->head.length; ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); } if (cfg->flags & IPFW_CFG_GET_STATIC) { /* We've requested static rules */ if (ctlv->head.type == IPFW_TLV_RULE_LIST) { rbase = (ipfw_obj_tlv *)(ctlv + 1); rcnt = ctlv->count; readsz += ctlv->head.length; ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); } } if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz)) { /* We may have some dynamic states */ dynsz = sz - readsz; /* Skip empty header */ if (dynsz != sizeof(ipfw_obj_ctlv)) dynbase = (caddr_t)ctlv; else dynsz = 0; } prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz); bp_alloc(&bp, 4096); /* if no rule numbers were specified, list all rules */ if (ac == 0) { fo->first = 0; fo->last = IPFW_DEFAULT_RULE; if (cfg->flags & IPFW_CFG_GET_STATIC) list_static_range(co, fo, &bp, rbase, rcnt); if (co->do_dynamic && dynsz > 0) { printf("## Dynamic rules (%d %zu):\n", fo->dcnt, dynsz); list_dyn_range(co, fo, &bp, dynbase, dynsz); } bp_free(&bp); return (EX_OK); } /* display specific rules requested on command line */ for (lac = ac, lav = av; lac != 0; lac--) { /* convert command line rule # */ fo->last = fo->first = strtoul(*lav++, &endptr, 10); if (*endptr == '-') fo->last = strtoul(endptr + 1, &endptr, 10); if (*endptr) { exitval = EX_USAGE; warnx("invalid rule number: %s", *(lav - 1)); continue; } if ((cfg->flags & IPFW_CFG_GET_STATIC) == 0) continue; if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) { /* give precedence to other error(s) */ if (exitval == EX_OK) exitval = EX_UNAVAILABLE; if (fo->first == fo->last) warnx("rule %u does not exist", fo->first); else warnx("no rules in range %u-%u", fo->first, fo->last); } } if (co->do_dynamic && dynsz > 0) { printf("## Dynamic rules:\n"); for (lac = ac, lav = av; lac != 0; lac--) { fo->last = fo->first = strtoul(*lav++, &endptr, 10); if (*endptr == '-') fo->last = strtoul(endptr+1, &endptr, 10); if (*endptr) /* already warned */ continue; list_dyn_range(co, fo, &bp, dynbase, dynsz); } } bp_free(&bp); return (exitval); } /* * Retrieves current ipfw configuration of given type * and stores its pointer to @pcfg. * * Caller is responsible for freeing @pcfg. * * Returns 0 on success. */ static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader **pcfg, size_t *psize) { ipfw_cfg_lheader *cfg; size_t sz; int i; if (co->test_only != 0) { fprintf(stderr, "Testing only, list disabled\n"); return (0); } /* Start with some data size */ sz = 4096; cfg = NULL; for (i = 0; i < 16; i++) { if (cfg != NULL) free(cfg); if ((cfg = calloc(1, sz)) == NULL) return (ENOMEM); cfg->flags = fo->flags; cfg->start_rule = fo->first; cfg->end_rule = fo->last; if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) { if (errno != ENOMEM) { free(cfg); return (errno); } /* Buffer size is not enough. Try to increase */ sz = sz * 2; if (sz < cfg->size) sz = cfg->size; continue; } *pcfg = cfg; *psize = sz; return (0); } free(cfg); return (ENOMEM); } static int lookup_host (char *host, struct in_addr *ipaddr) { struct hostent *he; if (!inet_aton(host, ipaddr)) { if ((he = gethostbyname(host)) == NULL) return(-1); *ipaddr = *(struct in_addr *)he->h_addr_list[0]; } return(0); } struct tidx { ipfw_obj_ntlv *idx; uint32_t count; uint32_t size; uint16_t counter; uint8_t set; }; int ipfw_check_object_name(const char *name) { int c, i, l; /* * Check that name is null-terminated and contains * valid symbols only. Valid mask is: * [a-zA-Z0-9\-_\.]{1,63} */ l = strlen(name); if (l == 0 || l >= 64) return (EINVAL); for (i = 0; i < l; i++) { c = name[i]; if (isalpha(c) || isdigit(c) || c == '_' || c == '-' || c == '.') continue; return (EINVAL); } return (0); } -static char *default_state_name = "default"; +static const char *default_state_name = "default"; + static int state_check_name(const char *name) { if (ipfw_check_object_name(name) != 0) return (EINVAL); if (strcmp(name, "any") == 0) return (EINVAL); return (0); } static int eaction_check_name(const char *name) { if (ipfw_check_object_name(name) != 0) return (EINVAL); /* Restrict some 'special' names */ if (match_token(rule_actions, name) != -1 && match_token(rule_action_params, name) != -1) return (EINVAL); return (0); } static uint16_t -pack_object(struct tidx *tstate, char *name, int otype) +pack_object(struct tidx *tstate, const char *name, int otype) { - int i; ipfw_obj_ntlv *ntlv; + uint32_t i; for (i = 0; i < tstate->count; i++) { if (strcmp(tstate->idx[i].name, name) != 0) continue; if (tstate->idx[i].set != tstate->set) continue; if (tstate->idx[i].head.type != otype) continue; return (tstate->idx[i].idx); } if (tstate->count + 1 > tstate->size) { tstate->size += 4; tstate->idx = realloc(tstate->idx, tstate->size * sizeof(ipfw_obj_ntlv)); if (tstate->idx == NULL) return (0); } ntlv = &tstate->idx[i]; memset(ntlv, 0, sizeof(ipfw_obj_ntlv)); strlcpy(ntlv->name, name, sizeof(ntlv->name)); ntlv->head.type = otype; ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->set = tstate->set; ntlv->idx = ++tstate->counter; tstate->count++; return (ntlv->idx); } static uint16_t -pack_table(struct tidx *tstate, char *name) +pack_table(struct tidx *tstate, const char *name) { if (table_check_name(name) != 0) return (0); return (pack_object(tstate, name, IPFW_TLV_TBL_NAME)); } void fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) { uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; uint16_t uidx; char *p; if ((p = strchr(av + 6, ')')) == NULL) errx(EX_DATAERR, "forgotten parenthesis: '%s'", av); *p = '\0'; p = strchr(av + 6, ','); if (p) *p++ = '\0'; if ((uidx = pack_table(tstate, av + 6)) == 0) errx(EX_DATAERR, "Invalid table name: %s", av + 6); cmd->opcode = opcode; cmd->arg1 = uidx; if (p) { cmd->len |= F_INSN_SIZE(ipfw_insn_u32); d[0] = strtoul(p, NULL, 0); } else cmd->len |= F_INSN_SIZE(ipfw_insn); } /* * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. * The following formats are allowed: * me returns O_IP_*_ME * 1.2.3.4 single IP address * 1.2.3.4:5.6.7.8 address:mask * 1.2.3.4/24 address/mask * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet * We can have multiple comma-separated address/mask entries. */ static void fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate) { int len = 0; uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; cmd->o.len &= ~F_LEN_MASK; /* zero len */ if (_substrcmp(av, "any") == 0) return; if (_substrcmp(av, "me") == 0) { cmd->o.len |= F_INSN_SIZE(ipfw_insn); return; } if (strncmp(av, "table(", 6) == 0) { fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate); return; } while (av) { /* * After the address we can have '/' or ':' indicating a mask, * ',' indicating another address follows, '{' indicating a * set of addresses of unspecified size. */ char *t = NULL, *p = strpbrk(av, "/:,{"); int masklen; char md, nd = '\0'; - CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn) + 2 + len); + CHECK_LENGTH(cblen, (int)F_INSN_SIZE(ipfw_insn) + 2 + len); if (p) { md = *p; *p++ = '\0'; if ((t = strpbrk(p, ",{")) != NULL) { nd = *t; *t = '\0'; } } else md = '\0'; if (lookup_host(av, (struct in_addr *)&d[0]) != 0) errx(EX_NOHOST, "hostname ``%s'' unknown", av); switch (md) { case ':': if (!inet_aton(p, (struct in_addr *)&d[1])) errx(EX_DATAERR, "bad netmask ``%s''", p); break; case '/': masklen = atoi(p); if (masklen == 0) d[1] = htonl(0U); /* mask */ else if (masklen > 32) errx(EX_DATAERR, "bad width ``%s''", p); else d[1] = htonl(~0U << (32 - masklen)); break; case '{': /* no mask, assume /24 and put back the '{' */ d[1] = htonl(~0U << (32 - 24)); *(--p) = md; break; case ',': /* single address plus continuation */ *(--p) = md; /* FALLTHROUGH */ case 0: /* initialization value */ default: d[1] = htonl(~0U); /* force /32 */ break; } d[0] &= d[1]; /* mask base address with mask */ if (t) *t = nd; /* find next separator */ if (p) p = strpbrk(p, ",{"); if (p && *p == '{') { /* * We have a set of addresses. They are stored as follows: * arg1 is the set size (powers of 2, 2..256) * addr is the base address IN HOST FORMAT * mask.. is an array of arg1 bits (rounded up to * the next multiple of 32) with bits set * for each host in the map. */ uint32_t *map = (uint32_t *)&cmd->mask; int low, high; int i = contigmask((uint8_t *)&(d[1]), 32); if (len > 0) errx(EX_DATAERR, "address set cannot be in a list"); if (i < 24 || i > 31) errx(EX_DATAERR, "invalid set with mask %d\n", i); cmd->o.arg1 = 1<<(32-i); /* map length */ d[0] = ntohl(d[0]); /* base addr in host format */ cmd->o.opcode = O_IP_DST_SET; /* default */ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) map[i] = 0; /* clear map */ av = p + 1; low = d[0] & 0xff; high = low + cmd->o.arg1 - 1; /* * Here, i stores the previous value when we specify a range * of addresses within a mask, e.g. 45-63. i = -1 means we * have no previous value. */ i = -1; /* previous value in a range */ while (isdigit(*av)) { char *s; int a = strtol(av, &s, 0); if (s == av) { /* no parameter */ if (*av != '}') errx(EX_DATAERR, "set not closed\n"); if (i != -1) errx(EX_DATAERR, "incomplete range %d-", i); break; } if (a < low || a > high) errx(EX_DATAERR, "addr %d out of range [%d-%d]\n", a, low, high); a -= low; if (i == -1) /* no previous in range */ i = a; else { /* check that range is valid */ if (i > a) errx(EX_DATAERR, "invalid range %d-%d", i+low, a+low); if (*s == '-') errx(EX_DATAERR, "double '-' in range"); } for (; i <= a; i++) map[i/32] |= 1<<(i & 31); i = -1; if (*s == '-') i = a; else if (*s == '}') break; av = s+1; } return; } av = p; if (av) /* then *av must be a ',' */ av++; /* Check this entry */ if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */ /* * 'any' turns the entire list into a NOP. * 'not any' never matches, so it is removed from the * list unless it is the only item, in which case we * report an error. */ if (cmd->o.len & F_NOT) { /* "not any" never matches */ if (av == NULL && len == 0) /* only this entry */ errx(EX_DATAERR, "not any never matches"); } /* else do nothing and skip this entry */ return; } /* A single IP can be stored in an optimized format */ if (d[1] == (uint32_t)~0 && av == NULL && len == 0) { cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); return; } len += 2; /* two words... */ d += 2; } /* end while */ if (len + 1 > F_LEN_MASK) errx(EX_DATAERR, "address list too long"); cmd->o.len |= len+1; } /* n2mask sets n bits of the mask */ void n2mask(struct in6_addr *mask, int n) { static int minimask[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; u_char *p; memset(mask, 0, sizeof(struct in6_addr)); p = (u_char *) mask; for (; n > 0; p++, n -= 8) { if (n >= 8) *p = 0xff; else *p = minimask[n]; } return; } static void fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, struct _s_x *flags, char *p) { char *e; uint32_t set = 0, clear = 0; if (fill_flags(flags, p, &e, &set, &clear) != 0) errx(EX_DATAERR, "invalid flag %s", e); cmd->opcode = opcode; cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); } void ipfw_delete(char *av[]) { ipfw_range_tlv rt; char *sep; int i, j; int exitval = EX_OK; int do_set = 0; av++; NEED1("missing rule specification"); if ( *av && _substrcmp(*av, "set") == 0) { /* Do not allow using the following syntax: * ipfw set N delete set M */ - if (co.use_set) + if (g_co.use_set) errx(EX_DATAERR, "invalid syntax"); do_set = 1; /* delete set */ av++; } /* Rule number */ while (*av && isdigit(**av)) { i = strtol(*av, &sep, 10); j = i; if (*sep== '-') j = strtol(sep + 1, NULL, 10); av++; - if (co.do_nat) { + if (g_co.do_nat) { exitval = ipfw_delete_nat(i); - } else if (co.do_pipe) { - exitval = ipfw_delete_pipe(co.do_pipe, i); + } else if (g_co.do_pipe) { + exitval = ipfw_delete_pipe(g_co.do_pipe, i); } else { memset(&rt, 0, sizeof(rt)); if (do_set != 0) { rt.set = i & 31; rt.flags = IPFW_RCFLAG_SET; } else { rt.start_rule = i & 0xffff; rt.end_rule = j & 0xffff; if (rt.start_rule == 0 && rt.end_rule == 0) rt.flags |= IPFW_RCFLAG_ALL; else rt.flags |= IPFW_RCFLAG_RANGE; - if (co.use_set != 0) { - rt.set = co.use_set - 1; + if (g_co.use_set != 0) { + rt.set = g_co.use_set - 1; rt.flags |= IPFW_RCFLAG_SET; } } - if (co.do_dynamic == 2) + if (g_co.do_dynamic == 2) rt.flags |= IPFW_RCFLAG_DYNAMIC; i = do_range_cmd(IP_FW_XDEL, &rt); if (i != 0) { exitval = EX_UNAVAILABLE; - if (co.do_quiet) + if (g_co.do_quiet) continue; warn("rule %u: setsockopt(IP_FW_XDEL)", rt.start_rule); } else if (rt.new_set == 0 && do_set == 0 && - co.do_dynamic != 2) { + g_co.do_dynamic != 2) { exitval = EX_UNAVAILABLE; - if (co.do_quiet) + if (g_co.do_quiet) continue; if (rt.start_rule != rt.end_rule) warnx("no rules rules in %u-%u range", rt.start_rule, rt.end_rule); else warnx("rule %u not found", rt.start_rule); } } } - if (exitval != EX_OK && co.do_force == 0) + if (exitval != EX_OK && g_co.do_force == 0) exit(exitval); } /* * fill the interface structure. We do not check the name as we can * create interfaces dynamically, so checking them at insert time * makes relatively little sense. * Interface names containing '*', '?', or '[' are assumed to be shell * patterns which match interfaces. */ static void fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate) { char *p; uint16_t uidx; cmd->name[0] = '\0'; cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); CHECK_CMDLEN; /* Parse the interface or address */ if (strcmp(arg, "any") == 0) cmd->o.len = 0; /* effectively ignore this command */ else if (strncmp(arg, "table(", 6) == 0) { if ((p = strchr(arg + 6, ')')) == NULL) errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg); *p = '\0'; p = strchr(arg + 6, ','); if (p) *p++ = '\0'; if ((uidx = pack_table(tstate, arg + 6)) == 0) errx(EX_DATAERR, "Invalid table name: %s", arg + 6); cmd->name[0] = '\1'; /* Special value indicating table */ cmd->p.kidx = uidx; } else if (!isdigit(*arg)) { strlcpy(cmd->name, arg, sizeof(cmd->name)); cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; } else if (!inet_aton(arg, &cmd->p.ip)) errx(EX_DATAERR, "bad ip address ``%s''", arg); } static void get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask) { int i; size_t l; char *ap, *ptr, *optr; struct ether_addr *mac; const char *macset = "0123456789abcdefABCDEF:"; if (strcmp(p, "any") == 0) { for (i = 0; i < ETHER_ADDR_LEN; i++) addr[i] = mask[i] = 0; return; } optr = ptr = strdup(p); if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) { l = strlen(ap); if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL) errx(EX_DATAERR, "Incorrect MAC address"); bcopy(mac, addr, ETHER_ADDR_LEN); } else errx(EX_DATAERR, "Incorrect MAC address"); if (ptr != NULL) { /* we have mask? */ if (p[ptr - optr - 1] == '/') { /* mask len */ long ml = strtol(ptr, &ap, 10); if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0) errx(EX_DATAERR, "Incorrect mask length"); for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++) mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml); } else { /* mask */ l = strlen(ptr); if (strspn(ptr, macset) != l || (mac = ether_aton(ptr)) == NULL) errx(EX_DATAERR, "Incorrect mask"); bcopy(mac, mask, ETHER_ADDR_LEN); } } else { /* default mask: ff:ff:ff:ff:ff:ff */ for (i = 0; i < ETHER_ADDR_LEN; i++) mask[i] = 0xff; } for (i = 0; i < ETHER_ADDR_LEN; i++) addr[i] &= mask[i]; free(optr); } /* * helper function, updates the pointer to cmd with the length * of the current command, and also cleans up the first word of * the new command in case it has been clobbered before. */ static ipfw_insn * next_cmd(ipfw_insn *cmd, int *len) { *len -= F_LEN(cmd); CHECK_LENGTH(*len, 0); cmd += F_LEN(cmd); bzero(cmd, sizeof(*cmd)); return cmd; } /* * Takes arguments and copies them into a comment */ static void fill_comment(ipfw_insn *cmd, char **av, int cblen) { int i, l; char *p = (char *)(cmd + 1); cmd->opcode = O_NOP; cmd->len = (cmd->len & (F_NOT | F_OR)); /* Compute length of comment string. */ for (i = 0, l = 0; av[i] != NULL; i++) l += strlen(av[i]) + 1; if (l == 0) return; if (l > 84) errx(EX_DATAERR, "comment too long (max 80 chars)"); l = 1 + (l+3)/4; cmd->len = (cmd->len & (F_NOT | F_OR)) | l; CHECK_CMDLEN; for (i = 0; av[i] != NULL; i++) { strcpy(p, av[i]); p += strlen(av[i]); *p++ = ' '; } *(--p) = '\0'; } /* * A function to fill simple commands of size 1. * Existing flags are preserved. */ static void fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) { cmd->opcode = opcode; cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; cmd->arg1 = arg; } /* * Fetch and add the MAC address and type, with masks. This generates one or * two microinstructions, and returns the pointer to the last one. */ static ipfw_insn * add_mac(ipfw_insn *cmd, char *av[], int cblen) { ipfw_insn_mac *mac; if ( ( av[0] == NULL ) || ( av[1] == NULL ) ) errx(EX_DATAERR, "MAC dst src"); cmd->opcode = O_MACADDR2; cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); CHECK_CMDLEN; mac = (ipfw_insn_mac *)cmd; get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]), &(mac->mask[ETHER_ADDR_LEN])); /* src */ return cmd; } static ipfw_insn * add_mactype(ipfw_insn *cmd, char *av, int cblen) { if (!av) errx(EX_DATAERR, "missing MAC type"); if (strcmp(av, "any") != 0) { /* we have a non-null type */ fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE, cblen); cmd->opcode = O_MAC_TYPE; return cmd; } else return NULL; } static ipfw_insn * add_proto0(ipfw_insn *cmd, char *av, u_char *protop) { struct protoent *pe; char *ep; int proto; proto = strtol(av, &ep, 10); if (*ep != '\0' || proto <= 0) { if ((pe = getprotobyname(av)) == NULL) return NULL; proto = pe->p_proto; } fill_cmd(cmd, O_PROTO, 0, proto); *protop = proto; return cmd; } static ipfw_insn * add_proto(ipfw_insn *cmd, char *av, u_char *protop) { u_char proto = IPPROTO_IP; if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) ; /* do not set O_IP4 nor O_IP6 */ else if (strcmp(av, "ip4") == 0) /* explicit "just IPv4" rule */ fill_cmd(cmd, O_IP4, 0, 0); else if (strcmp(av, "ip6") == 0) { /* explicit "just IPv6" rule */ proto = IPPROTO_IPV6; fill_cmd(cmd, O_IP6, 0, 0); } else return add_proto0(cmd, av, protop); *protop = proto; return cmd; } static ipfw_insn * add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop) { u_char proto = IPPROTO_IP; if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) ; /* do not set O_IP4 nor O_IP6 */ else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0) /* explicit "just IPv4" rule */ fill_cmd(cmd, O_IP4, 0, 0); else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) { /* explicit "just IPv6" rule */ proto = IPPROTO_IPV6; fill_cmd(cmd, O_IP6, 0, 0); } else return add_proto0(cmd, av, protop); *protop = proto; return cmd; } static ipfw_insn * add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ cmd->opcode = O_IP_SRC_SET; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ cmd->opcode = O_IP_SRC_LOOKUP; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ cmd->opcode = O_IP_SRC_ME; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ cmd->opcode = O_IP_SRC; else /* addr/mask */ cmd->opcode = O_IP_SRC_MASK; return cmd; } static ipfw_insn * add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ ; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ ; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ cmd->opcode = O_IP_DST_ME; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ cmd->opcode = O_IP_DST; else /* addr/mask */ cmd->opcode = O_IP_DST_MASK; return cmd; } static struct _s_x f_reserved_keywords[] = { { "altq", TOK_OR }, { "//", TOK_OR }, { "diverted", TOK_OR }, { "dst-port", TOK_OR }, { "src-port", TOK_OR }, { "established", TOK_OR }, { "keep-state", TOK_OR }, { "frag", TOK_OR }, { "icmptypes", TOK_OR }, { "in", TOK_OR }, { "out", TOK_OR }, { "ip6", TOK_OR }, { "any", TOK_OR }, { "to", TOK_OR }, { "via", TOK_OR }, { "{", TOK_OR }, { NULL, 0 } /* terminator */ }; static ipfw_insn * add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen) { if (match_token(f_reserved_keywords, av) != -1) return (NULL); if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) { /* XXX todo: check that we have a protocol with ports */ cmd->opcode = opcode; return cmd; } return NULL; } static ipfw_insn * add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) { struct in6_addr a; char *host, *ch, buf[INET6_ADDRSTRLEN]; ipfw_insn *ret = NULL; - int len; + size_t len; /* Copy first address in set if needed */ if ((ch = strpbrk(av, "/,")) != NULL) { len = ch - av; strlcpy(buf, av, sizeof(buf)); if (len < sizeof(buf)) buf[len] = '\0'; host = buf; } else host = av; if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || inet_pton(AF_INET6, host, &a) == 1) ret = add_srcip6(cmd, av, cblen, tstate); /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) ret = add_srcip(cmd, av, cblen, tstate); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; return ret; } static ipfw_insn * add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) { struct in6_addr a; char *host, *ch, buf[INET6_ADDRSTRLEN]; ipfw_insn *ret = NULL; - int len; + size_t len; /* Copy first address in set if needed */ if ((ch = strpbrk(av, "/,")) != NULL) { len = ch - av; strlcpy(buf, av, sizeof(buf)); if (len < sizeof(buf)) buf[len] = '\0'; host = buf; } else host = av; if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || inet_pton(AF_INET6, host, &a) == 1) ret = add_dstip6(cmd, av, cblen, tstate); /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) ret = add_dstip(cmd, av, cblen, tstate); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; return ret; } /* * Parse arguments and assemble the microinstructions which make up a rule. * Rules are added into the 'rulebuf' and then copied in the correct order * into the actual rule. * * The syntax for a rule starts with the action, followed by * optional action parameters, and the various match patterns. * In the assembled microcode, the first opcode must be an O_PROBE_STATE * (generated if the rule includes a keep-state option), then the * various match patterns, log/altq actions, and the actual action. * */ -void +static void compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) { /* * rules are added into the 'rulebuf' and then copied in * the correct order into the actual rule. * Some things that need to go out of order (prob, action etc.) * go into actbuf[]. */ static uint32_t actbuf[255], cmdbuf[255]; int rblen, ablen, cblen; ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; ipfw_insn *first_cmd; /* first match pattern */ struct ip_fw_rule *rule; /* * various flags used to record that we entered some fields. */ ipfw_insn *have_state = NULL; /* any state-related option */ int have_rstate = 0; ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL; ipfw_insn *have_skipcmd = NULL; size_t len; int i; int open_par = 0; /* open parenthesis ( */ /* proto is here because it is used to fetch ports */ u_char proto = IPPROTO_IP; /* default protocol */ double match_prob = 1; /* match probability, default is always match */ bzero(actbuf, sizeof(actbuf)); /* actions go here */ bzero(cmdbuf, sizeof(cmdbuf)); bzero(rbuf, *rbufsize); rule = (struct ip_fw_rule *)rbuf; cmd = (ipfw_insn *)cmdbuf; action = (ipfw_insn *)actbuf; rblen = *rbufsize / sizeof(uint32_t); rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t); ablen = sizeof(actbuf) / sizeof(actbuf[0]); cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]); cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1; #define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; } #define CHECK_ACTLEN CHECK_LENGTH(ablen, action->len) av++; /* [rule N] -- Rule number optional */ if (av[0] && isdigit(**av)) { rule->rulenum = atoi(*av); av++; } /* [set N] -- set number (0..RESVD_SET), optional */ if (av[0] && av[1] && _substrcmp(*av, "set") == 0) { int set = strtoul(av[1], NULL, 10); if (set < 0 || set > RESVD_SET) errx(EX_DATAERR, "illegal set %s", av[1]); rule->set = set; tstate->set = set; av += 2; } /* [prob D] -- match probability, optional */ if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) { match_prob = strtod(av[1], NULL); if (match_prob <= 0 || match_prob > 1) errx(EX_DATAERR, "illegal match prob. %s", av[1]); av += 2; } /* action -- mandatory */ NEED1("missing action"); i = match_token(rule_actions, *av); av++; action->len = 1; /* default */ CHECK_ACTLEN; switch(i) { case TOK_CHECKSTATE: have_state = action; action->opcode = O_CHECK_STATE; if (*av == NULL || match_token(rule_options, *av) == TOK_COMMENT) { action->arg1 = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } if (*av[0] == ':') { if (strcmp(*av + 1, "any") == 0) action->arg1 = 0; else if (state_check_name(*av + 1) == 0) action->arg1 = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); else errx(EX_DATAERR, "Invalid state name %s", *av); av++; break; } errx(EX_DATAERR, "Invalid state name %s", *av); break; case TOK_ABORT: action->opcode = O_REJECT; action->arg1 = ICMP_REJECT_ABORT; break; case TOK_ABORT6: action->opcode = O_UNREACH6; action->arg1 = ICMP6_UNREACH_ABORT; break; case TOK_ACCEPT: action->opcode = O_ACCEPT; break; case TOK_DENY: action->opcode = O_DENY; action->arg1 = 0; break; case TOK_REJECT: action->opcode = O_REJECT; action->arg1 = ICMP_UNREACH_HOST; break; case TOK_RESET: action->opcode = O_REJECT; action->arg1 = ICMP_REJECT_RST; break; case TOK_RESET6: action->opcode = O_UNREACH6; action->arg1 = ICMP6_UNREACH_RST; break; case TOK_UNREACH: action->opcode = O_REJECT; NEED1("missing reject code"); fill_reject_code(&action->arg1, *av); av++; break; case TOK_UNREACH6: action->opcode = O_UNREACH6; NEED1("missing unreach code"); fill_unreach6_code(&action->arg1, *av); av++; break; case TOK_COUNT: action->opcode = O_COUNT; break; case TOK_NAT: action->opcode = O_NAT; action->len = F_INSN_SIZE(ipfw_insn_nat); CHECK_ACTLEN; if (*av != NULL && _substrcmp(*av, "global") == 0) { action->arg1 = IP_FW_NAT44_GLOBAL; av++; break; } else goto chkarg; case TOK_QUEUE: action->opcode = O_QUEUE; goto chkarg; case TOK_PIPE: action->opcode = O_PIPE; goto chkarg; case TOK_SKIPTO: action->opcode = O_SKIPTO; goto chkarg; case TOK_NETGRAPH: action->opcode = O_NETGRAPH; goto chkarg; case TOK_NGTEE: action->opcode = O_NGTEE; goto chkarg; case TOK_DIVERT: action->opcode = O_DIVERT; goto chkarg; case TOK_TEE: action->opcode = O_TEE; goto chkarg; case TOK_CALL: action->opcode = O_CALLRETURN; chkarg: if (!av[0]) errx(EX_USAGE, "missing argument for %s", *(av - 1)); if (isdigit(**av)) { action->arg1 = strtoul(*av, NULL, 10); if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG) errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); } else if (_substrcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else if (i == TOK_DIVERT || i == TOK_TEE) { struct servent *s; setservent(1); s = getservbyname(av[0], "divert"); if (s != NULL) action->arg1 = ntohs(s->s_port); else errx(EX_DATAERR, "illegal divert/tee port"); } else errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); av++; break; case TOK_FORWARD: { /* * Locate the address-port separator (':' or ','). * Could be one of the following: * hostname:port * IPv4 a.b.c.d,port * IPv4 a.b.c.d:port * IPv6 w:x:y::z,port * IPv6 [w:x:y::z]:port */ struct sockaddr_storage result; struct addrinfo *res; char *s, *end; int family; u_short port_number; NEED1("missing forward address[:port]"); if (_substrcmp(*av, "tablearg") == 0) { family = PF_INET; ((struct sockaddr_in*)&result)->sin_addr.s_addr = INADDR_ANY; } else { /* * Are we an bracket-enclosed IPv6 address? */ if (strchr(*av, '[')) (*av)++; /* * locate the address-port separator (':' or ',') */ s = strchr(*av, ','); if (s == NULL) { s = strchr(*av, ']'); /* Prevent erroneous parsing on brackets. */ if (s != NULL) *(s++) = '\0'; else s = *av; /* Distinguish between IPv4:port and IPv6 cases. */ s = strchr(s, ':'); if (s && strchr(s+1, ':')) s = NULL; /* no port */ } if (s != NULL) { /* Terminate host portion and set s to start of port. */ *(s++) = '\0'; i = strtoport(s, &end, 0 /* base */, 0 /* proto */); if (s == end) errx(EX_DATAERR, "illegal forwarding port ``%s''", s); port_number = (u_short)i; } /* * Resolve the host name or address to a family and a * network representation of the address. */ if (getaddrinfo(*av, NULL, NULL, &res)) errx(EX_DATAERR, NULL); /* Just use the first host in the answer. */ family = res->ai_family; memcpy(&result, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } if (family == PF_INET) { ipfw_insn_sa *p = (ipfw_insn_sa *)action; action->opcode = O_FORWARD_IP; action->len = F_INSN_SIZE(ipfw_insn_sa); CHECK_ACTLEN; /* * In the kernel we assume AF_INET and use only * sin_port and sin_addr. Remember to set sin_len as * the routing code seems to use it too. */ p->sa.sin_len = sizeof(struct sockaddr_in); p->sa.sin_family = AF_INET; p->sa.sin_port = port_number; p->sa.sin_addr.s_addr = ((struct sockaddr_in *)&result)->sin_addr.s_addr; } else if (family == PF_INET6) { ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action; action->opcode = O_FORWARD_IP6; action->len = F_INSN_SIZE(ipfw_insn_sa6); CHECK_ACTLEN; p->sa.sin6_len = sizeof(struct sockaddr_in6); p->sa.sin6_family = AF_INET6; p->sa.sin6_port = port_number; p->sa.sin6_flowinfo = 0; p->sa.sin6_scope_id = ((struct sockaddr_in6 *)&result)->sin6_scope_id; bcopy(&((struct sockaddr_in6*)&result)->sin6_addr, &p->sa.sin6_addr, sizeof(p->sa.sin6_addr)); } else { errx(EX_DATAERR, "Invalid address family in forward action"); } av++; break; } case TOK_COMMENT: /* pretend it is a 'count' rule followed by the comment */ action->opcode = O_COUNT; av--; /* go back... */ break; case TOK_SETFIB: { int numfibs; size_t intsize = sizeof(int); action->opcode = O_SETFIB; NEED1("missing fib number"); if (_substrcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else { action->arg1 = strtoul(*av, NULL, 10); if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) errx(EX_DATAERR, "fibs not suported.\n"); if (action->arg1 >= numfibs) /* Temporary */ errx(EX_DATAERR, "fib too large.\n"); /* Add high-order bit to fib to make room for tablearg*/ action->arg1 |= 0x8000; } av++; break; } case TOK_SETDSCP: { int code; action->opcode = O_SETDSCP; NEED1("missing DSCP code"); if (_substrcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else { if (isalpha(*av[0])) { if ((code = match_token(f_ipdscp, *av)) == -1) errx(EX_DATAERR, "Unknown DSCP code"); action->arg1 = code; } else action->arg1 = strtoul(*av, NULL, 10); /* * Add high-order bit to DSCP to make room * for tablearg */ action->arg1 |= 0x8000; } av++; break; } case TOK_REASS: action->opcode = O_REASS; break; case TOK_RETURN: fill_cmd(action, O_CALLRETURN, F_NOT, 0); break; case TOK_TCPSETMSS: { u_long mss; uint16_t idx; idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); NEED1("Missing MSS value"); action = next_cmd(action, &ablen); action->len = 1; CHECK_ACTLEN; mss = strtoul(*av, NULL, 10); if (mss == 0 || mss > UINT16_MAX) errx(EX_USAGE, "invalid MSS value %s", *av); fill_cmd(action, O_EXTERNAL_DATA, 0, (uint16_t)mss); av++; break; } default: av--; if (match_token(rule_eactions, *av) == -1) errx(EX_DATAERR, "invalid action %s\n", *av); /* * External actions support. * XXX: we support only syntax with instance name. * For known external actions (from rule_eactions list) * we can handle syntax directly. But with `eaction' * keyword we can use only `eaction ' * syntax. */ case TOK_EACTION: { uint16_t idx; NEED1("Missing eaction name"); if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction name %s", *av); idx = pack_object(tstate, *av, IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); av++; NEED1("Missing eaction instance name"); action = next_cmd(action, &ablen); action->len = 1; CHECK_ACTLEN; if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction instance name %s", *av); /* * External action instance object has TLV type depended * from the external action name object index. Since we * currently don't know this index, use zero as TLV type. */ idx = pack_object(tstate, *av, 0); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx); av++; } } action = next_cmd(action, &ablen); /* * [altq queuename] -- altq tag, optional * [log [logamount N]] -- log, optional * * If they exist, it go first in the cmdbuf, but then it is * skipped in the copy section to the end of the buffer. */ while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) { av++; switch (i) { case TOK_LOG: { ipfw_insn_log *c = (ipfw_insn_log *)cmd; int l; if (have_log) errx(EX_DATAERR, "log cannot be specified more than once"); have_log = (ipfw_insn *)c; cmd->len = F_INSN_SIZE(ipfw_insn_log); CHECK_CMDLEN; cmd->opcode = O_LOG; if (av[0] && _substrcmp(*av, "logamount") == 0) { av++; NEED1("logamount requires argument"); l = atoi(*av); if (l < 0) errx(EX_DATAERR, "logamount must be positive"); c->max_log = l; av++; } else { len = sizeof(c->max_log); if (sysctlbyname("net.inet.ip.fw.verbose_limit", &c->max_log, &len, NULL, 0) == -1) { - if (co.test_only) { + if (g_co.test_only) { c->max_log = 0; break; } errx(1, "sysctlbyname(\"%s\")", "net.inet.ip.fw.verbose_limit"); } } } break; #ifndef NO_ALTQ case TOK_ALTQ: { ipfw_insn_altq *a = (ipfw_insn_altq *)cmd; NEED1("missing altq queue name"); if (have_altq) errx(EX_DATAERR, "altq cannot be specified more than once"); have_altq = (ipfw_insn *)a; cmd->len = F_INSN_SIZE(ipfw_insn_altq); CHECK_CMDLEN; cmd->opcode = O_ALTQ; a->qid = altq_name_to_qid(*av); av++; } break; #endif case TOK_TAG: case TOK_UNTAG: { uint16_t tag; if (have_tag) errx(EX_USAGE, "tag and untag cannot be " "specified more than once"); GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i, rule_action_params); have_tag = cmd; fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag); av++; break; } default: abort(); } cmd = next_cmd(cmd, &cblen); } if (have_state) { /* must be a check-state, we are done */ if (*av != NULL && match_token(rule_options, *av) == TOK_COMMENT) { /* check-state has a comment */ av++; fill_comment(cmd, av, cblen); cmd = next_cmd(cmd, &cblen); av[0] = NULL; } goto done; } #define OR_START(target) \ if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \ if (open_par) \ errx(EX_USAGE, "nested \"(\" not allowed\n"); \ prev = NULL; \ open_par = 1; \ if ( (av[0])[1] == '\0') { \ av++; \ } else \ (*av)++; \ } \ target: \ #define CLOSE_PAR \ if (open_par) { \ if (av[0] && ( \ strcmp(*av, ")") == 0 || \ strcmp(*av, "}") == 0)) { \ prev = NULL; \ open_par = 0; \ av++; \ } else \ errx(EX_USAGE, "missing \")\"\n"); \ } #define NOT_BLOCK \ if (av[0] && _substrcmp(*av, "not") == 0) { \ if (cmd->len & F_NOT) \ errx(EX_USAGE, "double \"not\" not allowed\n"); \ cmd->len |= F_NOT; \ av++; \ } #define OR_BLOCK(target) \ if (av[0] && _substrcmp(*av, "or") == 0) { \ if (prev == NULL || open_par == 0) \ errx(EX_DATAERR, "invalid OR block"); \ prev->len |= F_OR; \ av++; \ goto target; \ } \ CLOSE_PAR; first_cmd = cmd; #if 0 /* * MAC addresses, optional. * If we have this, we skip the part "proto from src to dst" * and jump straight to the option parsing. */ NOT_BLOCK; NEED1("missing protocol"); if (_substrcmp(*av, "MAC") == 0 || _substrcmp(*av, "mac") == 0) { av++; /* the "MAC" keyword */ add_mac(cmd, av); /* exits in case of errors */ cmd = next_cmd(cmd); av += 2; /* dst-mac and src-mac */ NOT_BLOCK; NEED1("missing mac type"); if (add_mactype(cmd, av[0])) cmd = next_cmd(cmd); av++; /* any or mac-type */ goto read_options; } #endif /* * protocol, mandatory */ OR_START(get_proto); NOT_BLOCK; NEED1("missing protocol"); if (add_proto_compat(cmd, *av, &proto)) { av++; if (F_LEN(cmd) != 0) { prev = cmd; cmd = next_cmd(cmd, &cblen); } } else if (first_cmd != cmd) { errx(EX_DATAERR, "invalid protocol ``%s''", *av); } else { rule->flags |= IPFW_RULE_JUSTOPTS; goto read_options; } OR_BLOCK(get_proto); first_cmd = cmd; /* update pointer to use in compact form */ /* * "from", mandatory */ if ((av[0] == NULL) || _substrcmp(*av, "from") != 0) errx(EX_USAGE, "missing ``from''"); av++; /* * source IP, mandatory */ OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); if (add_src(cmd, *av, proto, cblen, tstate)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd, &cblen); } } else errx(EX_USAGE, "bad source address %s", *av); OR_BLOCK(source_ip); /* * source ports, optional */ NOT_BLOCK; /* optional "not" */ if ( av[0] != NULL ) { if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { av++; if (F_LEN(cmd) != 0) cmd = next_cmd(cmd, &cblen); } } /* * "to", mandatory */ if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 ) errx(EX_USAGE, "missing ``to''"); av++; /* * destination, mandatory */ OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); if (add_dst(cmd, *av, proto, cblen, tstate)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd, &cblen); } } else errx( EX_USAGE, "bad destination address %s", *av); OR_BLOCK(dest_ip); /* * dest. ports, optional */ NOT_BLOCK; /* optional "not" */ if (av[0]) { if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { av++; if (F_LEN(cmd) != 0) cmd = next_cmd(cmd, &cblen); } } if (first_cmd == cmd) rule->flags |= IPFW_RULE_NOOPT; read_options: prev = NULL; while ( av[0] != NULL ) { char *s; ipfw_insn_u32 *cmd32; /* alias for cmd */ s = *av; cmd32 = (ipfw_insn_u32 *)cmd; if (*s == '!') { /* alternate syntax for NOT */ if (cmd->len & F_NOT) errx(EX_USAGE, "double \"not\" not allowed\n"); cmd->len = F_NOT; s++; } i = match_token(rule_options, s); av++; switch(i) { case TOK_NOT: if (cmd->len & F_NOT) errx(EX_USAGE, "double \"not\" not allowed\n"); cmd->len = F_NOT; break; case TOK_OR: if (open_par == 0 || prev == NULL) errx(EX_USAGE, "invalid \"or\" block\n"); prev->len |= F_OR; break; case TOK_STARTBRACE: if (open_par) errx(EX_USAGE, "+nested \"(\" not allowed\n"); open_par = 1; break; case TOK_ENDBRACE: if (!open_par) errx(EX_USAGE, "+missing \")\"\n"); open_par = 0; prev = NULL; break; case TOK_IN: fill_cmd(cmd, O_IN, 0, 0); break; case TOK_OUT: cmd->len ^= F_NOT; /* toggle F_NOT */ fill_cmd(cmd, O_IN, 0, 0); break; case TOK_DIVERTED: fill_cmd(cmd, O_DIVERTED, 0, 3); break; case TOK_DIVERTEDLOOPBACK: fill_cmd(cmd, O_DIVERTED, 0, 1); break; case TOK_DIVERTEDOUTPUT: fill_cmd(cmd, O_DIVERTED, 0, 2); break; case TOK_FRAG: fill_cmd(cmd, O_FRAG, 0, 0); break; case TOK_LAYER2: fill_cmd(cmd, O_LAYER2, 0, 0); break; case TOK_XMIT: case TOK_RECV: case TOK_VIA: NEED1("recv, xmit, via require interface name" " or address"); fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate); av++; if (F_LEN(cmd) == 0) /* not a valid address */ break; if (i == TOK_XMIT) cmd->opcode = O_XMIT; else if (i == TOK_RECV) cmd->opcode = O_RECV; else if (i == TOK_VIA) cmd->opcode = O_VIA; break; case TOK_ICMPTYPES: NEED1("icmptypes requires list of types"); fill_icmptypes((ipfw_insn_u32 *)cmd, *av); av++; break; case TOK_ICMP6TYPES: NEED1("icmptypes requires list of types"); fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen); av++; break; case TOK_IPTTL: NEED1("ipttl requires TTL"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_IPTTL, cblen)) errx(EX_DATAERR, "invalid ipttl %s", *av); } else fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPID: NEED1("ipid requires id"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_IPID, cblen)) errx(EX_DATAERR, "invalid ipid %s", *av); } else fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPLEN: NEED1("iplen requires length"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_IPLEN, cblen)) errx(EX_DATAERR, "invalid ip len %s", *av); } else fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPVER: NEED1("ipver requires version"); fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPPRECEDENCE: NEED1("ipprecedence requires value"); fill_cmd(cmd, O_IPPRECEDENCE, 0, (strtoul(*av, NULL, 0) & 7) << 5); av++; break; case TOK_DSCP: NEED1("missing DSCP code"); fill_dscp(cmd, *av, cblen); av++; break; case TOK_IPOPTS: NEED1("missing argument for ipoptions"); fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av); av++; break; case TOK_IPTOS: NEED1("missing argument for iptos"); fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av); av++; break; case TOK_UID: NEED1("uid requires argument"); { char *end; uid_t uid; struct passwd *pwd; cmd->opcode = O_UID; uid = strtoul(*av, &end, 0); pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); if (pwd == NULL) errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); cmd32->d[0] = pwd->pw_uid; cmd->len |= F_INSN_SIZE(ipfw_insn_u32); av++; } break; case TOK_GID: NEED1("gid requires argument"); { char *end; gid_t gid; struct group *grp; cmd->opcode = O_GID; gid = strtoul(*av, &end, 0); grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); if (grp == NULL) errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); cmd32->d[0] = grp->gr_gid; cmd->len |= F_INSN_SIZE(ipfw_insn_u32); av++; } break; case TOK_JAIL: NEED1("jail requires argument"); { char *end; int jid; cmd->opcode = O_JAIL; /* * If av is a number, then we'll just pass it as-is. If * it's a name, try to resolve that to a jid. * * We save the jail_getid(3) call for a fallback because * it entails an unconditional trip to the kernel to * either validate a jid or resolve a name to a jid. * This specific token doesn't currently require a * jid to be an active jail, so we save a transition * by simply using a number that we're given. */ jid = strtoul(*av, &end, 10); if (*end != '\0') { jid = jail_getid(*av); if (jid < 0) errx(EX_DATAERR, "%s", jail_errmsg); } cmd32->d[0] = (uint32_t)jid; cmd->len |= F_INSN_SIZE(ipfw_insn_u32); av++; } break; case TOK_ESTAB: fill_cmd(cmd, O_ESTAB, 0, 0); break; case TOK_SETUP: fill_cmd(cmd, O_TCPFLAGS, 0, (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); break; case TOK_TCPDATALEN: NEED1("tcpdatalen requires length"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen)) errx(EX_DATAERR, "invalid tcpdata len %s", *av); } else fill_cmd(cmd, O_TCPDATALEN, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_TCPOPTS: NEED1("missing argument for tcpoptions"); fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av); av++; break; case TOK_TCPSEQ: case TOK_TCPACK: NEED1("tcpseq/tcpack requires argument"); cmd->len = F_INSN_SIZE(ipfw_insn_u32); cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); av++; break; case TOK_TCPMSS: case TOK_TCPWIN: NEED1("tcpmss/tcpwin requires size"); if (strpbrk(*av, "-,")) { if (add_ports(cmd, *av, 0, i == TOK_TCPWIN ? O_TCPWIN : O_TCPMSS, cblen) == NULL) errx(EX_DATAERR, "invalid %s size %s", s, *av); } else fill_cmd(cmd, i == TOK_TCPWIN ? O_TCPWIN : O_TCPMSS, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_TCPFLAGS: NEED1("missing argument for tcpflags"); cmd->opcode = O_TCPFLAGS; fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av); av++; break; case TOK_KEEPSTATE: case TOK_RECORDSTATE: { uint16_t uidx; if (open_par) errx(EX_USAGE, "keep-state or record-state cannot be part " "of an or block"); if (have_state) errx(EX_USAGE, "only one of keep-state, record-state, " " limit and set-limit is allowed"); if (*av != NULL && *av[0] == ':') { if (state_check_name(*av + 1) != 0) errx(EX_DATAERR, "Invalid state name %s", *av); uidx = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); av++; } else uidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); have_state = cmd; have_rstate = i == TOK_RECORDSTATE; fill_cmd(cmd, O_KEEP_STATE, 0, uidx); break; } case TOK_LIMIT: case TOK_SETLIMIT: { ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; int val; if (open_par) errx(EX_USAGE, "limit or set-limit cannot be part of an or block"); if (have_state) errx(EX_USAGE, "only one of keep-state, record-state, " " limit and set-limit is allowed"); have_state = cmd; have_rstate = i == TOK_SETLIMIT; cmd->len = F_INSN_SIZE(ipfw_insn_limit); CHECK_CMDLEN; cmd->opcode = O_LIMIT; c->limit_mask = c->conn_limit = 0; while ( av[0] != NULL ) { if ((val = match_token(limit_masks, *av)) <= 0) break; c->limit_mask |= val; av++; } if (c->limit_mask == 0) errx(EX_USAGE, "limit: missing limit mask"); GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX, TOK_LIMIT, rule_options); av++; if (*av != NULL && *av[0] == ':') { if (state_check_name(*av + 1) != 0) errx(EX_DATAERR, "Invalid state name %s", *av); cmd->arg1 = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); av++; } else cmd->arg1 = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } case TOK_PROTO: NEED1("missing protocol"); if (add_proto(cmd, *av, &proto)) { av++; } else errx(EX_DATAERR, "invalid protocol ``%s''", *av); break; case TOK_SRCIP: NEED1("missing source IP"); if (add_srcip(cmd, *av, cblen, tstate)) { av++; } break; case TOK_DSTIP: NEED1("missing destination IP"); if (add_dstip(cmd, *av, cblen, tstate)) { av++; } break; case TOK_SRCIP6: NEED1("missing source IP6"); if (add_srcip6(cmd, *av, cblen, tstate)) { av++; } break; case TOK_DSTIP6: NEED1("missing destination IP6"); if (add_dstip6(cmd, *av, cblen, tstate)) { av++; } break; case TOK_SRCPORT: NEED1("missing source port"); if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { av++; } else errx(EX_DATAERR, "invalid source port %s", *av); break; case TOK_DSTPORT: NEED1("missing destination port"); if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { av++; } else errx(EX_DATAERR, "invalid destination port %s", *av); break; case TOK_MAC: if (add_mac(cmd, av, cblen)) av += 2; break; case TOK_MACTYPE: NEED1("missing mac type"); if (!add_mactype(cmd, *av, cblen)) errx(EX_DATAERR, "invalid mac type %s", *av); av++; break; case TOK_VERREVPATH: fill_cmd(cmd, O_VERREVPATH, 0, 0); break; case TOK_VERSRCREACH: fill_cmd(cmd, O_VERSRCREACH, 0, 0); break; case TOK_ANTISPOOF: fill_cmd(cmd, O_ANTISPOOF, 0, 0); break; case TOK_IPSEC: fill_cmd(cmd, O_IPSEC, 0, 0); break; case TOK_IPV6: fill_cmd(cmd, O_IP6, 0, 0); break; case TOK_IPV4: fill_cmd(cmd, O_IP4, 0, 0); break; case TOK_EXT6HDR: fill_ext6hdr( cmd, *av ); av++; break; case TOK_FLOWID: if (proto != IPPROTO_IPV6 ) errx( EX_USAGE, "flow-id filter is active " "only for ipv6 protocol\n"); fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen); av++; break; case TOK_COMMENT: fill_comment(cmd, av, cblen); av[0]=NULL; break; case TOK_TAGGED: if (av[0] && strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_TAGGED, cblen)) errx(EX_DATAERR, "tagged: invalid tag" " list: %s", *av); } else { uint16_t tag; GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, TOK_TAGGED, rule_options); fill_cmd(cmd, O_TAGGED, 0, tag); } av++; break; case TOK_FIB: NEED1("fib requires fib number"); fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_SOCKARG: fill_cmd(cmd, O_SOCKARG, 0, 0); break; case TOK_LOOKUP: { ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; int j; if (!av[0] || !av[1]) errx(EX_USAGE, "format: lookup argument tablenum"); cmd->opcode = O_IP_DST_LOOKUP; cmd->len |= F_INSN_SIZE(ipfw_insn) + 2; i = match_token(rule_options, *av); for (j = 0; lookup_key[j] >= 0 ; j++) { if (i == lookup_key[j]) break; } if (lookup_key[j] <= 0) errx(EX_USAGE, "format: cannot lookup on %s", *av); __PAST_END(c->d, 1) = j; // i converted to option av++; if ((j = pack_table(tstate, *av)) == 0) errx(EX_DATAERR, "Invalid table name: %s", *av); cmd->arg1 = j; av++; } break; case TOK_FLOW: NEED1("missing table name"); if (strncmp(*av, "table(", 6) != 0) errx(EX_DATAERR, "enclose table name into \"table()\""); fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate); av++; break; case TOK_SKIPACTION: if (have_skipcmd) errx(EX_USAGE, "only one defer-action " "is allowed"); have_skipcmd = cmd; fill_cmd(cmd, O_SKIP_ACTION, 0, 0); break; default: errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); } if (F_LEN(cmd) > 0) { /* prepare to advance */ prev = cmd; cmd = next_cmd(cmd, &cblen); } } done: if (!have_state && have_skipcmd) warnx("Rule contains \"defer-immediate-action\" " "and doesn't contain any state-related options."); /* * Now copy stuff into the rule. * If we have a keep-state option, the first instruction * must be a PROBE_STATE (which is generated here). * If we have a LOG option, it was stored as the first command, * and now must be moved to the top of the action part. */ dst = (ipfw_insn *)rule->cmd; /* * First thing to write into the command stream is the match probability. */ if (match_prob != 1) { /* 1 means always match */ dst->opcode = O_PROB; dst->len = 2; *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); dst += dst->len; } /* * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) { fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); dst = next_cmd(dst, &rblen); } /* * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG, * O_SKIP_ACTION */ for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { i = F_LEN(src); CHECK_RBUFLEN(i); switch (src->opcode) { case O_LOG: case O_KEEP_STATE: case O_LIMIT: case O_ALTQ: case O_TAG: case O_SKIP_ACTION: break; default: bcopy(src, dst, i * sizeof(uint32_t)); dst += i; } } /* * put back the have_state command as last opcode */ if (have_state && have_state->opcode != O_CHECK_STATE) { i = F_LEN(have_state); CHECK_RBUFLEN(i); bcopy(have_state, dst, i * sizeof(uint32_t)); dst += i; } /* * put back the have_skipcmd command as very last opcode */ if (have_skipcmd) { i = F_LEN(have_skipcmd); CHECK_RBUFLEN(i); bcopy(have_skipcmd, dst, i * sizeof(uint32_t)); dst += i; } /* * start action section */ rule->act_ofs = dst - rule->cmd; /* put back O_LOG, O_ALTQ, O_TAG if necessary */ if (have_log) { i = F_LEN(have_log); CHECK_RBUFLEN(i); bcopy(have_log, dst, i * sizeof(uint32_t)); dst += i; } if (have_altq) { i = F_LEN(have_altq); CHECK_RBUFLEN(i); bcopy(have_altq, dst, i * sizeof(uint32_t)); dst += i; } if (have_tag) { i = F_LEN(have_tag); CHECK_RBUFLEN(i); bcopy(have_tag, dst, i * sizeof(uint32_t)); dst += i; } /* * copy all other actions */ for (src = (ipfw_insn *)actbuf; src != action; src += i) { i = F_LEN(src); CHECK_RBUFLEN(i); bcopy(src, dst, i * sizeof(uint32_t)); dst += i; } rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); *rbufsize = (char *)dst - (char *)rule; } static int compare_ntlv(const void *_a, const void *_b) { - ipfw_obj_ntlv *a, *b; + const ipfw_obj_ntlv *a, *b; - a = (ipfw_obj_ntlv *)_a; - b = (ipfw_obj_ntlv *)_b; + a = (const ipfw_obj_ntlv *)_a; + b = (const ipfw_obj_ntlv *)_b; if (a->set < b->set) return (-1); else if (a->set > b->set) return (1); if (a->idx < b->idx) return (-1); else if (a->idx > b->idx) return (1); if (a->head.type < b->head.type) return (-1); else if (a->head.type > b->head.type) return (1); return (0); } /* * Provide kernel with sorted list of referenced objects */ static void object_sort_ctlv(ipfw_obj_ctlv *ctlv) { qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv); } struct object_kt { uint16_t uidx; uint16_t type; }; static int compare_object_kntlv(const void *k, const void *v) { - ipfw_obj_ntlv *ntlv; + const ipfw_obj_ntlv *ntlv; struct object_kt key; - key = *((struct object_kt *)k); - ntlv = (ipfw_obj_ntlv *)v; + key = *((const struct object_kt *)k); + ntlv = (const ipfw_obj_ntlv *)v; if (key.uidx < ntlv->idx) return (-1); else if (key.uidx > ntlv->idx) return (1); if (key.type < ntlv->head.type) return (-1); else if (key.type > ntlv->head.type) return (1); return (0); } /* * Finds object name in @ctlv by @idx and @type. * Uses the following facts: * 1) All TLVs are the same size * 2) Kernel implementation provides already sorted list. * * Returns table name or NULL. */ static char * object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type) { ipfw_obj_ntlv *ntlv; struct object_kt key; key.uidx = idx; key.type = type; ntlv = bsearch(&key, (ctlv + 1), ctlv->count, ctlv->objsize, compare_object_kntlv); if (ntlv != NULL) return (ntlv->name); return (NULL); } static char * table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx) { return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME)); } /* * Adds one or more rules to ipfw chain. * Data layout: * Request: * [ * ip_fw3_opheader * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3) * ] * Reply: * [ * ip_fw3_opheader * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] * ] * * Rules in reply are modified to store their actual ruleset number. * * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending * according to their idx field and there has to be no duplicates. * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. * (*3) Each ip_fw structure needs to be aligned to u64 boundary. */ void ipfw_add(char *av[]) { uint32_t rulebuf[1024]; int rbufsize, default_off, tlen, rlen; size_t sz; struct tidx ts; struct ip_fw_rule *rule; caddr_t tbuf; ip_fw3_opheader *op3; ipfw_obj_ctlv *ctlv, *tstate; rbufsize = sizeof(rulebuf); memset(rulebuf, 0, rbufsize); memset(&ts, 0, sizeof(ts)); /* Optimize case with no tables */ default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader); op3 = (ip_fw3_opheader *)rulebuf; ctlv = (ipfw_obj_ctlv *)(op3 + 1); rule = (struct ip_fw_rule *)(ctlv + 1); rbufsize -= default_off; compile_rule(av, (uint32_t *)rule, &rbufsize, &ts); /* Align rule size to u64 boundary */ rlen = roundup2(rbufsize, sizeof(uint64_t)); tbuf = NULL; sz = 0; tstate = NULL; if (ts.count != 0) { /* Some tables. We have to alloc more data */ tlen = ts.count * sizeof(ipfw_obj_ntlv); sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen; if ((tbuf = calloc(1, sz)) == NULL) err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD"); op3 = (ip_fw3_opheader *)tbuf; /* Tables first */ ctlv = (ipfw_obj_ctlv *)(op3 + 1); ctlv->head.type = IPFW_TLV_TBLNAME_LIST; ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen; ctlv->count = ts.count; ctlv->objsize = sizeof(ipfw_obj_ntlv); memcpy(ctlv + 1, ts.idx, tlen); object_sort_ctlv(ctlv); tstate = ctlv; /* Rule next */ ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); ctlv->head.type = IPFW_TLV_RULE_LIST; ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; ctlv->count = 1; memcpy(ctlv + 1, rule, rbufsize); } else { /* Simply add header */ sz = rlen + default_off; memset(ctlv, 0, sizeof(*ctlv)); ctlv->head.type = IPFW_TLV_RULE_LIST; ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; ctlv->count = 1; } if (do_get3(IP_FW_XADD, op3, &sz) != 0) err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD"); - if (!co.do_quiet) { + if (!g_co.do_quiet) { struct format_opts sfo; struct buf_pr bp; memset(&sfo, 0, sizeof(sfo)); sfo.tstate = tstate; sfo.set_mask = (uint32_t)(-1); bp_alloc(&bp, 4096); - show_static_rule(&co, &sfo, &bp, rule, NULL); + show_static_rule(&g_co, &sfo, &bp, rule, NULL); printf("%s", bp.buf); bp_free(&bp); } if (tbuf != NULL) free(tbuf); if (ts.idx != NULL) free(ts.idx); } /* * clear the counters or the log counters. * optname has the following values: * 0 (zero both counters and logging) * 1 (zero logging only) */ void ipfw_zero(int ac, char *av[], int optname) { ipfw_range_tlv rt; char const *errstr; char const *name = optname ? "RESETLOG" : "ZERO"; uint32_t arg; int failed = EX_OK; optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO; av++; ac--; if (ac == 0) { /* clear all entries */ memset(&rt, 0, sizeof(rt)); rt.flags = IPFW_RCFLAG_ALL; if (do_range_cmd(optname, &rt) < 0) err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name); - if (!co.do_quiet) + if (!g_co.do_quiet) printf("%s.\n", optname == IP_FW_XZERO ? "Accounting cleared":"Logging counts reset"); return; } while (ac) { /* Rule number */ if (isdigit(**av)) { arg = strtonum(*av, 0, 0xffff, &errstr); if (errstr) errx(EX_DATAERR, "invalid rule number %s\n", *av); memset(&rt, 0, sizeof(rt)); rt.start_rule = arg; rt.end_rule = arg; rt.flags |= IPFW_RCFLAG_RANGE; - if (co.use_set != 0) { - rt.set = co.use_set - 1; + if (g_co.use_set != 0) { + rt.set = g_co.use_set - 1; rt.flags |= IPFW_RCFLAG_SET; } if (do_range_cmd(optname, &rt) != 0) { warn("rule %u: setsockopt(IP_FW_X%s)", arg, name); failed = EX_UNAVAILABLE; } else if (rt.new_set == 0) { printf("Entry %d not found\n", arg); failed = EX_UNAVAILABLE; - } else if (!co.do_quiet) + } else if (!g_co.do_quiet) printf("Entry %d %s.\n", arg, optname == IP_FW_XZERO ? "cleared" : "logging count reset"); } else { errx(EX_USAGE, "invalid rule number ``%s''", *av); } av++; ac--; } if (failed != EX_OK) exit(failed); } void ipfw_flush(int force) { ipfw_range_tlv rt; - if (!force && !co.do_quiet) { /* need to ask user */ + if (!force && !g_co.do_quiet) { /* need to ask user */ int c; printf("Are you sure? [yn] "); fflush(stdout); do { c = toupper(getc(stdin)); while (c != '\n' && getc(stdin) != '\n') if (feof(stdin)) return; /* and do not flush */ } while (c != 'Y' && c != 'N'); printf("\n"); if (c == 'N') /* user said no */ return; } - if (co.do_pipe) { + if (g_co.do_pipe) { dummynet_flush(); return; } /* `ipfw set N flush` - is the same that `ipfw delete set N` */ memset(&rt, 0, sizeof(rt)); - if (co.use_set != 0) { - rt.set = co.use_set - 1; + if (g_co.use_set != 0) { + rt.set = g_co.use_set - 1; rt.flags = IPFW_RCFLAG_SET; } else rt.flags = IPFW_RCFLAG_ALL; if (do_range_cmd(IP_FW_XDEL, &rt) != 0) err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)"); - if (!co.do_quiet) - printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules"); + if (!g_co.do_quiet) + printf("Flushed all %s.\n", g_co.do_pipe ? "pipes" : "rules"); } static struct _s_x intcmds[] = { { "talist", TOK_TALIST }, { "iflist", TOK_IFLIST }, { "olist", TOK_OLIST }, { "vlist", TOK_VLIST }, { NULL, 0 } }; static struct _s_x otypes[] = { { "EACTION", IPFW_TLV_EACTION }, { "DYNSTATE", IPFW_TLV_STATE_NAME }, { NULL, 0 } }; static const char* lookup_eaction_name(ipfw_obj_ntlv *ntlv, int cnt, uint16_t type) { const char *name; int i; name = NULL; for (i = 0; i < cnt; i++) { if (ntlv[i].head.type != IPFW_TLV_EACTION) continue; if (IPFW_TLV_EACTION_NAME(ntlv[i].idx) != type) continue; name = ntlv[i].name; break; } return (name); } static void -ipfw_list_objects(int ac, char *av[]) +ipfw_list_objects(int ac __unused, char *av[] __unused) { ipfw_obj_lheader req, *olh; ipfw_obj_ntlv *ntlv; const char *name; size_t sz; - int i; + uint32_t i; memset(&req, 0, sizeof(req)); sz = sizeof(req); if (do_get3(IP_FW_DUMP_SRVOBJECTS, &req.opheader, &sz) != 0) if (errno != ENOMEM) return; sz = req.size; if ((olh = calloc(1, sz)) == NULL) return; olh->size = sz; if (do_get3(IP_FW_DUMP_SRVOBJECTS, &olh->opheader, &sz) != 0) { free(olh); return; } if (olh->count > 0) printf("Objects list:\n"); else printf("There are no objects\n"); ntlv = (ipfw_obj_ntlv *)(olh + 1); for (i = 0; i < olh->count; i++) { name = match_value(otypes, ntlv->head.type); if (name == NULL) name = lookup_eaction_name( (ipfw_obj_ntlv *)(olh + 1), olh->count, ntlv->head.type); if (name == NULL) printf(" kidx: %4d\ttype: %10d\tname: %s\n", ntlv->idx, ntlv->head.type, ntlv->name); else printf(" kidx: %4d\ttype: %10s\tname: %s\n", ntlv->idx, name, ntlv->name); ntlv++; } free(olh); } void ipfw_internal_handler(int ac, char *av[]) { int tcmd; ac--; av++; NEED1("internal cmd required"); if ((tcmd = match_token(intcmds, *av)) == -1) errx(EX_USAGE, "invalid internal sub-cmd: %s", *av); switch (tcmd) { case TOK_IFLIST: ipfw_list_tifaces(); break; case TOK_TALIST: ipfw_list_ta(ac, av); break; case TOK_OLIST: ipfw_list_objects(ac, av); break; case TOK_VLIST: ipfw_list_values(ac, av); break; } } static int ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh) { ipfw_obj_lheader req, *olh; size_t sz; memset(&req, 0, sizeof(req)); sz = sizeof(req); if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) { if (errno != ENOMEM) return (errno); } sz = req.size; if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) { free(olh); return (errno); } *polh = olh; return (0); } static int ifinfo_cmp(const void *a, const void *b) { - ipfw_iface_info *ia, *ib; + const ipfw_iface_info *ia, *ib; - ia = (ipfw_iface_info *)a; - ib = (ipfw_iface_info *)b; + ia = (const ipfw_iface_info *)a; + ib = (const ipfw_iface_info *)b; return (stringnum_cmp(ia->ifname, ib->ifname)); } /* * Retrieves table list from kernel, * optionally sorts it and calls requested function for each table. * Returns 0 on success. */ static void -ipfw_list_tifaces() +ipfw_list_tifaces(void) { ipfw_obj_lheader *olh; ipfw_iface_info *info; - int i, error; + uint32_t i; + int error; if ((error = ipfw_get_tracked_ifaces(&olh)) != 0) err(EX_OSERR, "Unable to request ipfw tracked interface list"); qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp); info = (ipfw_iface_info *)(olh + 1); for (i = 0; i < olh->count; i++) { if (info->flags & IPFW_IFFLAG_RESOLVED) printf("%s ifindex: %d refcount: %u changes: %u\n", info->ifname, info->ifindex, info->refcnt, info->gencnt); else printf("%s ifindex: unresolved refcount: %u changes: %u\n", info->ifname, info->refcnt, info->gencnt); info = (ipfw_iface_info *)((caddr_t)info + olh->objsize); } free(olh); } Index: head/sbin/ipfw/ipfw2.h =================================================================== --- head/sbin/ipfw/ipfw2.h (revision 363163) +++ head/sbin/ipfw/ipfw2.h (revision 363164) @@ -1,453 +1,453 @@ /*- * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ */ /* * Options that can be set on the command line. * When reading commands from a file, a subset of the options can also * be applied globally by specifying them before the file name. * After that, each line can contain its own option that changes * the global value. * XXX The context is not restored after each line. */ struct cmdline_opts { /* boolean options: */ int do_value_as_ip; /* show table value as IP */ int do_resolv; /* try to resolve all ip to names */ int do_time; /* Show time stamps */ int do_quiet; /* Be quiet in add and flush */ int do_pipe; /* this cmd refers to a pipe/queue/sched */ int do_nat; /* this cmd refers to a nat config */ int do_compact; /* show rules in compact mode */ int do_force; /* do not ask for confirmation */ int show_sets; /* display the set each rule belongs to */ int test_only; /* only check syntax */ int comment_only; /* only print action and comment */ int verbose; /* be verbose on some commands */ /* The options below can have multiple values. */ int do_dynamic; /* 1 - display dynamic rules */ /* 2 - display/delete only dynamic rules */ int do_sort; /* field to sort results (0 = no) */ /* valid fields are 1 and above */ - int use_set; /* work with specified set number */ + uint32_t use_set; /* work with specified set number */ /* 0 means all sets, otherwise apply to set use_set - 1 */ }; enum { TIMESTAMP_NONE = 0, TIMESTAMP_STRING, TIMESTAMP_NUMERIC, }; -extern struct cmdline_opts co; +extern struct cmdline_opts g_co; /* * _s_x is a structure that stores a string <-> token pairs, used in * various places in the parser. Entries are stored in arrays, * with an entry with s=NULL as terminator. * The search routines are match_token() and match_value(). * Often, an element with x=0 contains an error string. * */ struct _s_x { char const *s; int x; }; extern struct _s_x f_ipdscp[]; enum tokens { TOK_NULL=0, TOK_OR, TOK_NOT, TOK_STARTBRACE, TOK_ENDBRACE, TOK_ABORT6, TOK_ABORT, TOK_ACCEPT, TOK_COUNT, TOK_EACTION, TOK_PIPE, TOK_LINK, TOK_QUEUE, TOK_FLOWSET, TOK_SCHED, TOK_DIVERT, TOK_TEE, TOK_NETGRAPH, TOK_NGTEE, TOK_FORWARD, TOK_SKIPTO, TOK_DENY, TOK_REJECT, TOK_RESET, TOK_UNREACH, TOK_CHECKSTATE, TOK_NAT, TOK_REASS, TOK_CALL, TOK_RETURN, TOK_ALTQ, TOK_LOG, TOK_TAG, TOK_UNTAG, TOK_TAGGED, TOK_UID, TOK_GID, TOK_JAIL, TOK_IN, TOK_LIMIT, TOK_SETLIMIT, TOK_KEEPSTATE, TOK_RECORDSTATE, TOK_LAYER2, TOK_OUT, TOK_DIVERTED, TOK_DIVERTEDLOOPBACK, TOK_DIVERTEDOUTPUT, TOK_XMIT, TOK_RECV, TOK_VIA, TOK_FRAG, TOK_IPOPTS, TOK_IPLEN, TOK_IPID, TOK_IPPRECEDENCE, TOK_DSCP, TOK_IPTOS, TOK_IPTTL, TOK_IPVER, TOK_ESTAB, TOK_SETUP, TOK_TCPDATALEN, TOK_TCPFLAGS, TOK_TCPOPTS, TOK_TCPSEQ, TOK_TCPACK, TOK_TCPMSS, TOK_TCPWIN, TOK_ICMPTYPES, TOK_MAC, TOK_MACTYPE, TOK_VERREVPATH, TOK_VERSRCREACH, TOK_ANTISPOOF, TOK_IPSEC, TOK_COMMENT, TOK_PLR, TOK_NOERROR, TOK_BUCKETS, TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, TOK_ALL, TOK_MASK, TOK_FLOW_MASK, TOK_SCHED_MASK, TOK_BW, TOK_DELAY, TOK_PROFILE, TOK_BURST, TOK_RED, TOK_GRED, TOK_ECN, TOK_DROPTAIL, TOK_PROTO, #ifdef NEW_AQM /* AQM tokens*/ TOK_NO_ECN, TOK_CODEL, TOK_FQ_CODEL, TOK_TARGET, TOK_INTERVAL, TOK_FLOWS, TOK_QUANTUM, TOK_PIE, TOK_FQ_PIE, TOK_TUPDATE, TOK_MAX_BURST, TOK_MAX_ECNTH, TOK_ALPHA, TOK_BETA, TOK_CAPDROP, TOK_NO_CAPDROP, TOK_ONOFF, TOK_DRE, TOK_TS, TOK_DERAND, TOK_NO_DERAND, #endif /* dummynet tokens */ TOK_WEIGHT, TOK_LMAX, TOK_PRI, TOK_TYPE, TOK_SLOTSIZE, TOK_IP, TOK_IF, TOK_ALOG, TOK_DENY_INC, TOK_SAME_PORTS, TOK_UNREG_ONLY, TOK_UNREG_CGN, TOK_SKIP_GLOBAL, TOK_RESET_ADDR, TOK_ALIAS_REV, TOK_PROXY_ONLY, TOK_REDIR_ADDR, TOK_REDIR_PORT, TOK_REDIR_PROTO, TOK_IPV6, TOK_FLOWID, TOK_ICMP6TYPES, TOK_EXT6HDR, TOK_DSTIP6, TOK_SRCIP6, TOK_IPV4, TOK_UNREACH6, TOK_RESET6, TOK_FIB, TOK_SETFIB, TOK_LOOKUP, TOK_SOCKARG, TOK_SETDSCP, TOK_FLOW, TOK_IFLIST, /* Table tokens */ TOK_CREATE, TOK_DESTROY, TOK_LIST, TOK_INFO, TOK_DETAIL, TOK_MODIFY, TOK_FLUSH, TOK_SWAP, TOK_ADD, TOK_DEL, TOK_VALTYPE, TOK_ALGO, TOK_TALIST, TOK_ATOMIC, TOK_LOCK, TOK_UNLOCK, TOK_VLIST, TOK_OLIST, TOK_MISSING, TOK_ORFLUSH, /* NAT64 tokens */ TOK_NAT64STL, TOK_NAT64LSN, TOK_STATS, TOK_STATES, TOK_CONFIG, TOK_TABLE4, TOK_TABLE6, TOK_PREFIX4, TOK_PREFIX6, TOK_AGG_LEN, TOK_AGG_COUNT, TOK_MAX_PORTS, TOK_STATES_CHUNKS, TOK_JMAXLEN, TOK_PORT_RANGE, TOK_HOST_DEL_AGE, TOK_PG_DEL_AGE, TOK_TCP_SYN_AGE, TOK_TCP_CLOSE_AGE, TOK_TCP_EST_AGE, TOK_UDP_AGE, TOK_ICMP_AGE, TOK_LOGOFF, TOK_PRIVATE, TOK_PRIVATEOFF, /* NAT64 CLAT tokens */ TOK_NAT64CLAT, TOK_PLAT_PREFIX, TOK_CLAT_PREFIX, /* NPTv6 tokens */ TOK_NPTV6, TOK_INTPREFIX, TOK_EXTPREFIX, TOK_PREFIXLEN, TOK_EXTIF, TOK_TCPSETMSS, TOK_SKIPACTION, }; /* * the following macro returns an error message if we run out of * arguments. */ #define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);} #define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);} struct buf_pr { char *buf; /* allocated buffer */ char *ptr; /* current pointer */ size_t size; /* total buffer size */ size_t avail; /* available storage */ size_t needed; /* length needed */ }; int pr_u64(struct buf_pr *bp, uint64_t *pd, int width); int bp_alloc(struct buf_pr *b, size_t size); void bp_free(struct buf_pr *b); -int bprintf(struct buf_pr *b, char *format, ...); +int bprintf(struct buf_pr *b, const char *format, ...); /* memory allocation support */ void *safe_calloc(size_t number, size_t size); void *safe_realloc(void *ptr, size_t size); /* string comparison functions used for historical compatibility */ int _substrcmp(const char *str1, const char* str2); int _substrcmp2(const char *str1, const char* str2, const char* str3); int stringnum_cmp(const char *a, const char *b); /* utility functions */ int match_token(struct _s_x *table, const char *string); int match_token_relaxed(struct _s_x *table, const char *string); int get_token(struct _s_x *table, const char *string, const char *errbase); char const *match_value(struct _s_x *p, int value); size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table, - char *delimiter); + const char *delimiter); int fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set, uint32_t *clear); void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set); struct _ip_fw3_opheader; int do_cmd(int optname, void *optval, uintptr_t optlen); int do_set3(int optname, struct _ip_fw3_opheader *op3, size_t optlen); int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen); struct in6_addr; void n2mask(struct in6_addr *mask, int n); -int contigmask(uint8_t *p, int len); +int contigmask(const uint8_t *p, int len); /* * Forward declarations to avoid include way too many headers. * C does not allow duplicated typedefs, so we use the base struct * that the typedef points to. * Should the typedefs use a different type, the compiler will * still detect the change when compiling the body of the * functions involved, so we do not lose error checking. */ struct _ipfw_insn; struct _ipfw_insn_altq; struct _ipfw_insn_u32; struct _ipfw_insn_ip6; struct _ipfw_insn_icmp6; /* * The reserved set numer. This is a constant in ip_fw.h * but we store it in a variable so other files do not depend * in that header just for one constant. */ extern int resvd_set_number; /* first-level command handlers */ void ipfw_add(char *av[]); void ipfw_show_nat(int ac, char **av); int ipfw_delete_nat(int i); void ipfw_config_pipe(int ac, char **av); void ipfw_config_nat(int ac, char **av); void ipfw_sets_handler(char *av[]); void ipfw_table_handler(int ac, char *av[]); void ipfw_sysctl_handler(char *av[], int which); void ipfw_delete(char *av[]); void ipfw_flush(int force); void ipfw_zero(int ac, char *av[], int optname); void ipfw_list(int ac, char *av[], int show_counters); void ipfw_internal_handler(int ac, char *av[]); void ipfw_nat64clat_handler(int ac, char *av[]); void ipfw_nat64lsn_handler(int ac, char *av[]); void ipfw_nat64stl_handler(int ac, char *av[]); void ipfw_nptv6_handler(int ac, char *av[]); int ipfw_check_object_name(const char *name); int ipfw_check_nat64prefix(const struct in6_addr *prefix, int length); #ifdef PF /* altq.c */ void altq_set_enabled(int enabled); u_int32_t altq_name_to_qid(const char *name); -void print_altq_cmd(struct buf_pr *bp, struct _ipfw_insn_altq *altqptr); +void print_altq_cmd(struct buf_pr *bp, const struct _ipfw_insn_altq *altqptr); #else #define NO_ALTQ #endif /* dummynet.c */ void dummynet_list(int ac, char *av[], int show_counters); void dummynet_flush(void); int ipfw_delete_pipe(int pipe_or_queue, int n); /* ipv6.c */ void print_unreach6_code(struct buf_pr *bp, uint16_t code); -void print_ip6(struct buf_pr *bp, struct _ipfw_insn_ip6 *cmd); -void print_flow6id(struct buf_pr *bp, struct _ipfw_insn_u32 *cmd); -void print_icmp6types(struct buf_pr *bp, struct _ipfw_insn_u32 *cmd); -void print_ext6hdr(struct buf_pr *bp, struct _ipfw_insn *cmd ); +void print_ip6(struct buf_pr *bp, const struct _ipfw_insn_ip6 *cmd); +void print_flow6id(struct buf_pr *bp, const struct _ipfw_insn_u32 *cmd); +void print_icmp6types(struct buf_pr *bp, const struct _ipfw_insn_u32 *cmd); +void print_ext6hdr(struct buf_pr *bp, const struct _ipfw_insn *cmd); struct tidx; struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate); struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate); void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen); void fill_unreach6_code(u_short *codep, char *str); void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen); int fill_ext6hdr(struct _ipfw_insn *cmd, char *av); /* ipfw2.c */ void bp_flush(struct buf_pr *b); void fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate); /* tables.c */ struct _ipfw_obj_ctlv; struct _ipfw_obj_ntlv; int table_check_name(const char *tablename); void ipfw_list_ta(int ac, char *av[]); void ipfw_list_values(int ac, char *av[]); void table_fill_ntlv(struct _ipfw_obj_ntlv *ntlv, const char *name, uint8_t set, uint16_t uidx); Index: head/sbin/ipfw/ipv6.c =================================================================== --- head/sbin/ipfw/ipv6.c (revision 363163) +++ head/sbin/ipfw/ipv6.c (revision 363164) @@ -1,519 +1,522 @@ /*- * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ * * ipv6 support */ #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define CHECK_LENGTH(v, len) do { \ if ((v) < (len)) \ errx(EX_DATAERR, "Rule too long"); \ } while (0) static struct _s_x icmp6codes[] = { { "no-route", ICMP6_DST_UNREACH_NOROUTE }, { "admin-prohib", ICMP6_DST_UNREACH_ADMIN }, { "address", ICMP6_DST_UNREACH_ADDR }, { "port", ICMP6_DST_UNREACH_NOPORT }, { NULL, 0 } }; void fill_unreach6_code(u_short *codep, char *str) { int val; char *s; val = strtoul(str, &s, 0); if (s == str || *s != '\0' || val >= 0x100) val = match_token(icmp6codes, str); if (val < 0) errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str); *codep = val; return; } void print_unreach6_code(struct buf_pr *bp, uint16_t code) { char const *s = match_value(icmp6codes, code); if (s != NULL) bprintf(bp, "unreach6 %s", s); else bprintf(bp, "unreach6 %u", code); } /* * Print the ip address contained in a command. */ void -print_ip6(struct buf_pr *bp, ipfw_insn_ip6 *cmd) +print_ip6(struct buf_pr *bp, const ipfw_insn_ip6 *cmd) { char trad[255]; struct hostent *he = NULL; - struct in6_addr *a = &(cmd->addr6); + const struct in6_addr *a = &(cmd->addr6); int len, mb; - len = F_LEN((ipfw_insn *) cmd) - 1; + len = F_LEN((const ipfw_insn *)cmd) - 1; if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) { bprintf(bp, " me6"); return; } if (cmd->o.opcode == O_IP6) { bprintf(bp, " ip6"); return; } /* * len == 4 indicates a single IP, whereas lists of 1 or more * addr/mask pairs have len = (2n+1). We convert len to n so we * use that to count the number of entries. */ bprintf(bp, " "); for (len = len / 4; len > 0; len -= 2, a += 2) { /* mask length */ mb = (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ? 128: - contigmask((uint8_t *)&(a[1]), 128); + contigmask((const uint8_t *)&(a[1]), 128); - if (mb == 128 && co.do_resolv) - he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6); + if (mb == 128 && g_co.do_resolv) + he = gethostbyaddr((const char *)a, sizeof(*a), + AF_INET6); if (he != NULL) /* resolved to name */ bprintf(bp, "%s", he->h_name); else if (mb == 0) /* any */ bprintf(bp, "any"); else { /* numeric IP followed by some kind of mask */ if (inet_ntop(AF_INET6, a, trad, sizeof(trad)) == NULL) bprintf(bp, "Error ntop in print_ip6\n"); bprintf(bp, "%s", trad ); if (mb < 0) /* mask not contiguous */ bprintf(bp, "/%s", inet_ntop(AF_INET6, &a[1], trad, sizeof(trad))); else if (mb < 128) bprintf(bp, "/%d", mb); } if (len > 2) bprintf(bp, ","); } } void fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen) { uint8_t type; - CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_icmp6)); + CHECK_LENGTH(cblen, (int)F_INSN_SIZE(ipfw_insn_icmp6)); memset(cmd, 0, sizeof(*cmd)); while (*av) { if (*av == ',') av++; type = strtoul(av, &av, 0); if (*av != ',' && *av != '\0') errx(EX_DATAERR, "invalid ICMP6 type"); /* * XXX: shouldn't this be 0xFF? I can't see any reason why * we shouldn't be able to filter all possiable values * regardless of the ability of the rest of the kernel to do * anything useful with them. */ if (type > ICMP6_MAXTYPE) errx(EX_DATAERR, "ICMP6 type out of range"); cmd->d[type / 32] |= ( 1 << (type % 32)); } cmd->o.opcode = O_ICMP6TYPE; cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6); } void -print_icmp6types(struct buf_pr *bp, ipfw_insn_u32 *cmd) +print_icmp6types(struct buf_pr *bp, const ipfw_insn_u32 *cmd) { int i, j; char sep= ' '; bprintf(bp, " icmp6types"); for (i = 0; i < 7; i++) for (j=0; j < 32; ++j) { if ( (cmd->d[i] & (1 << (j))) == 0) continue; bprintf(bp, "%c%d", sep, (i*32 + j)); sep = ','; } } void -print_flow6id(struct buf_pr *bp, ipfw_insn_u32 *cmd) +print_flow6id(struct buf_pr *bp, const ipfw_insn_u32 *cmd) { uint16_t i, limit = cmd->o.arg1; char sep = ','; bprintf(bp, " flow-id "); for( i=0; i < limit; ++i) { if (i == limit - 1) sep = ' '; bprintf(bp, "%d%c", cmd->d[i], sep); } } /* structure and define for the extension header in ipv6 */ static struct _s_x ext6hdrcodes[] = { { "frag", EXT_FRAGMENT }, { "hopopt", EXT_HOPOPTS }, { "route", EXT_ROUTING }, { "dstopt", EXT_DSTOPTS }, { "ah", EXT_AH }, { "esp", EXT_ESP }, { "rthdr0", EXT_RTHDR0 }, { "rthdr2", EXT_RTHDR2 }, { NULL, 0 } }; /* fills command for the extension header filtering */ int fill_ext6hdr( ipfw_insn *cmd, char *av) { int tok; char *s = av; cmd->arg1 = 0; while(s) { av = strsep( &s, ",") ; tok = match_token(ext6hdrcodes, av); switch (tok) { case EXT_FRAGMENT: cmd->arg1 |= EXT_FRAGMENT; break; case EXT_HOPOPTS: cmd->arg1 |= EXT_HOPOPTS; break; case EXT_ROUTING: cmd->arg1 |= EXT_ROUTING; break; case EXT_DSTOPTS: cmd->arg1 |= EXT_DSTOPTS; break; case EXT_AH: cmd->arg1 |= EXT_AH; break; case EXT_ESP: cmd->arg1 |= EXT_ESP; break; case EXT_RTHDR0: cmd->arg1 |= EXT_RTHDR0; break; case EXT_RTHDR2: cmd->arg1 |= EXT_RTHDR2; break; default: errx(EX_DATAERR, "invalid option for ipv6 exten header"); break; } } if (cmd->arg1 == 0) return (0); cmd->opcode = O_EXT_HDR; cmd->len |= F_INSN_SIZE(ipfw_insn); return (1); } void -print_ext6hdr(struct buf_pr *bp, ipfw_insn *cmd ) +print_ext6hdr(struct buf_pr *bp, const ipfw_insn *cmd ) { char sep = ' '; bprintf(bp, " extension header:"); if (cmd->arg1 & EXT_FRAGMENT) { bprintf(bp, "%cfragmentation", sep); sep = ','; } if (cmd->arg1 & EXT_HOPOPTS) { bprintf(bp, "%chop options", sep); sep = ','; } if (cmd->arg1 & EXT_ROUTING) { bprintf(bp, "%crouting options", sep); sep = ','; } if (cmd->arg1 & EXT_RTHDR0) { bprintf(bp, "%crthdr0", sep); sep = ','; } if (cmd->arg1 & EXT_RTHDR2) { bprintf(bp, "%crthdr2", sep); sep = ','; } if (cmd->arg1 & EXT_DSTOPTS) { bprintf(bp, "%cdestination options", sep); sep = ','; } if (cmd->arg1 & EXT_AH) { bprintf(bp, "%cauthentication header", sep); sep = ','; } if (cmd->arg1 & EXT_ESP) { bprintf(bp, "%cencapsulated security payload", sep); } } /* Try to find ipv6 address by hostname */ static int lookup_host6 (char *host, struct in6_addr *ip6addr) { struct hostent *he; if (!inet_pton(AF_INET6, host, ip6addr)) { if ((he = gethostbyname2(host, AF_INET6)) == NULL) return(-1); memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr)); } return (0); } /* * fill the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. * The following formats are allowed: * any matches any IP6. Actually returns an empty instruction. * me returns O_IP6_*_ME * * 03f1::234:123:0342 single IP6 address * 03f1::234:123:0342/24 address/masklen * 03f1::234:123:0342/ffff::ffff:ffff address/mask * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address * * Set of address (as in ipv6) not supported because ipv6 address * are typically random past the initial prefix. * Return 1 on success, 0 on failure. */ static int fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen, struct tidx *tstate) { int len = 0; struct in6_addr *d = &(cmd->addr6); char *oav; /* * Needed for multiple address. * Note d[1] points to struct in6_add r mask6 of cmd */ cmd->o.len &= ~F_LEN_MASK; /* zero len */ if (strcmp(av, "any") == 0) return (1); /* Set the data for "me" opt */ if (strcmp(av, "me") == 0 || strcmp(av, "me6") == 0) { cmd->o.len |= F_INSN_SIZE(ipfw_insn); return (1); } if (strncmp(av, "table(", 6) == 0) { fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate); return (1); } oav = av = strdup(av); while (av) { /* * After the address we can have '/' indicating a mask, * or ',' indicating another address follows. */ char *p, *q; int masklen; char md = '\0'; - CHECK_LENGTH(cblen, 1 + len + 2 * F_INSN_SIZE(struct in6_addr)); + CHECK_LENGTH(cblen, + 1 + len + 2 * (int)F_INSN_SIZE(struct in6_addr)); if ((q = strchr(av, ',')) ) { *q = '\0'; q++; } if ((p = strchr(av, '/')) ) { md = *p; /* save the separator */ *p = '\0'; /* terminate address string */ p++; /* and skip past it */ } /* now p points to NULL, mask or next entry */ /* lookup stores address in *d as a side effect */ if (lookup_host6(av, d) != 0) { /* XXX: failed. Free memory and go */ errx(EX_DATAERR, "bad address \"%s\"", av); } /* next, look at the mask, if any */ if (md == '/' && strchr(p, ':')) { if (!inet_pton(AF_INET6, p, &d[1])) errx(EX_DATAERR, "bad mask \"%s\"", p); masklen = contigmask((uint8_t *)&(d[1]), 128); } else { masklen = (md == '/') ? atoi(p) : 128; if (masklen > 128 || masklen < 0) errx(EX_DATAERR, "bad width \"%s\''", p); else n2mask(&d[1], masklen); } APPLY_MASK(d, &d[1]); /* mask base address with mask */ av = q; /* Check this entry */ if (masklen == 0) { /* * 'any' turns the entire list into a NOP. * 'not any' never matches, so it is removed from the * list unless it is the only item, in which case we * report an error. */ if (cmd->o.len & F_NOT && av == NULL && len == 0) errx(EX_DATAERR, "not any never matches"); continue; } /* * A single IP can be stored alone */ if (masklen == 128 && av == NULL && len == 0) { len = F_INSN_SIZE(struct in6_addr); break; } /* Update length and pointer to arguments */ len += F_INSN_SIZE(struct in6_addr)*2; d += 2; } /* end while */ /* * Total length of the command, remember that 1 is the size of * the base command. */ if (len + 1 > F_LEN_MASK) errx(EX_DATAERR, "address list too long"); cmd->o.len |= len+1; free(oav); return (1); } /* * fills command for ipv6 flow-id filtering * note that the 20 bit flow number is stored in a array of u_int32_t * it's supported lists of flow-id, so in the o.arg1 we store how many * additional flow-id we want to filter, the basic is 1 */ void fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen) { u_int32_t type; /* Current flow number */ u_int16_t nflow = 0; /* Current flow index */ char *s = av; cmd->d[0] = 0; /* Initializing the base number*/ while (s) { - CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_u32) + nflow + 1); + CHECK_LENGTH(cblen, + (int)F_INSN_SIZE(ipfw_insn_u32) + nflow + 1); av = strsep( &s, ",") ; type = strtoul(av, &av, 0); if (*av != ',' && *av != '\0') errx(EX_DATAERR, "invalid ipv6 flow number %s", av); if (type > 0xfffff) errx(EX_DATAERR, "flow number out of range %s", av); cmd->d[nflow] |= type; nflow++; } if( nflow > 0 ) { cmd->o.opcode = O_FLOW6ID; cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow; cmd->o.arg1 = nflow; } else { errx(EX_DATAERR, "invalid ipv6 flow number %s", av); } } ipfw_insn * add_srcip6(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ cmd->opcode = O_IP_SRC_SET; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ cmd->opcode = O_IP_SRC_LOOKUP; else if (F_LEN(cmd) == 0) { /* any */ } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ cmd->opcode = O_IP6_SRC_ME; } else if (F_LEN(cmd) == (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { /* single IP, no mask*/ cmd->opcode = O_IP6_SRC; } else { /* addr/mask opt */ cmd->opcode = O_IP6_SRC_MASK; } return cmd; } ipfw_insn * add_dstip6(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ ; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ ; else if (F_LEN(cmd) == 0) { /* any */ } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ cmd->opcode = O_IP6_DST_ME; } else if (F_LEN(cmd) == (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { /* single IP, no mask*/ cmd->opcode = O_IP6_DST; } else { /* addr/mask opt */ cmd->opcode = O_IP6_DST_MASK; } return cmd; } Index: head/sbin/ipfw/main.c =================================================================== --- head/sbin/ipfw/main.c (revision 363163) +++ head/sbin/ipfw/main.c (revision 363164) @@ -1,640 +1,640 @@ /*- * Copyright (c) 2002-2003,2010 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * Command line interface for IP firewall facility * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "ipfw2.h" static void help(void) { fprintf(stderr, "ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" "\tipfw [-abcdefhnNqStTv] \n\n" "where is one of the following:\n\n" "add [num] [set N] [prob x] RULE-BODY\n" "{pipe|queue} N config PIPE-BODY\n" "[pipe|queue] {zero|delete|show} [N{,N}]\n" "nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n" " reset|reverse|proxy_only|redirect_addr linkspec|\n" " redirect_port linkspec|redirect_proto linkspec}\n" "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" "set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n" "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" "table all {flush | list}\n" "\n" "RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" "ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" " skipto N | {divert|tee} PORT | forward ADDR |\n" " pipe N | queue N | nat N | setfib FIB | reass\n" "PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" "ADDR: [ MAC dst src ether_type ] \n" " [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" " [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" "IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" "IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" "IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" "IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" "OPTION_LIST: OPTION [OPTION_LIST]\n" "OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" " {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" " {dst-port|src-port} LIST |\n" " estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" " iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" " ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" " icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n" " mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" " tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" ); exit(0); } /* * Called with the arguments, including program name because getopt * wants it to be present. * Returns 0 if successful, 1 if empty command, errx() in case of errors. * First thing we do is process parameters creating an argv[] array * which includes the program name and a NULL entry at the end. * If we are called with a single string, we split it on whitespace. * Also, arguments with a trailing ',' are joined to the next one. * The pointers (av[]) and data are in a single chunk of memory. * av[0] points to the original program name, all other entries * point into the allocated chunk. */ static int ipfw_main(int oldac, char **oldav) { int ch, ac; const char *errstr; char **av, **save_av; int do_acct = 0; /* Show packet/byte count */ int try_next = 0; /* set if pipe cmd not found */ int av_size; /* compute the av size */ char *av_p; /* used to build the av list */ #define WHITESP " \t\f\v\n\r" if (oldac < 2) return 1; /* need at least one argument */ if (oldac == 2) { /* * If we are called with one argument, try to split it into * words for subsequent parsing. Spaces after a ',' are * removed by copying the string in-place. */ char *arg = oldav[1]; /* The string is the first arg. */ int l = strlen(arg); int copy = 0; /* 1 if we need to copy, 0 otherwise */ int i, j; for (i = j = 0; i < l; i++) { if (arg[i] == '#') /* comment marker */ break; if (copy) { arg[j++] = arg[i]; copy = !strchr("," WHITESP, arg[i]); } else { copy = !strchr(WHITESP, arg[i]); if (copy) arg[j++] = arg[i]; } } if (!copy && j > 0) /* last char was a 'blank', remove it */ j--; l = j; /* the new argument length */ arg[j++] = '\0'; if (l == 0) /* empty string! */ return 1; /* * First, count number of arguments. Because of the previous * processing, this is just the number of blanks plus 1. */ for (i = 0, ac = 1; i < l; i++) if (strchr(WHITESP, arg[i]) != NULL) ac++; /* * Allocate the argument list structure as a single block * of memory, containing pointers and the argument * strings. We include one entry for the program name * because getopt expects it, and a NULL at the end * to simplify further parsing. */ ac++; /* add 1 for the program name */ av_size = (ac+1) * sizeof(char *) + l + 1; av = safe_calloc(av_size, 1); /* * Init the argument pointer to the end of the array * and copy arguments from arg[] to av[]. For each one, * j is the initial character, i is the one past the end. */ av_p = (char *)&av[ac+1]; for (ac = 1, i = j = 0; i < l; i++) { if (strchr(WHITESP, arg[i]) != NULL || i == l-1) { if (i == l-1) i++; bcopy(arg+j, av_p, i-j); av[ac] = av_p; av_p += i-j; /* the length of the string */ *av_p++ = '\0'; ac++; j = i + 1; } } } else { /* * If an argument ends with ',' join with the next one. */ int first, i, l=0; /* * Allocate the argument list structure as a single block * of memory, containing both pointers and the argument * strings. We include some space for the program name * because getopt expects it. * We add an extra pointer to the end of the array, * to make simpler further parsing. */ for (i=0; i= 2 && !strcmp(av[1], "sysctl")) { char *s; int i; if (ac != 3) { printf( "sysctl emulation usage:\n" " ipfw sysctl name[=value]\n" " ipfw sysctl -a\n"); return 0; } s = strchr(av[2], '='); if (s == NULL) { s = !strcmp(av[2], "-a") ? NULL : av[2]; sysctlbyname(s, NULL, NULL, NULL, 0); } else { /* ipfw sysctl x.y.z=value */ /* assume an INT value, will extend later */ if (s[1] == '\0') { printf("ipfw sysctl: missing value\n\n"); return 0; } *s = '\0'; i = strtol(s+1, NULL, 0); sysctlbyname(av[2], NULL, NULL, &i, sizeof(int)); } return 0; } #endif /* Save arguments for final freeing of memory. */ save_av = av; optind = optreset = 1; /* restart getopt() */ while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtv")) != -1) switch (ch) { case 'a': do_acct = 1; break; case 'b': - co.comment_only = 1; - co.do_compact = 1; + g_co.comment_only = 1; + g_co.do_compact = 1; break; case 'c': - co.do_compact = 1; + g_co.do_compact = 1; break; case 'd': - co.do_dynamic = 1; + g_co.do_dynamic = 1; break; case 'D': - co.do_dynamic = 2; + g_co.do_dynamic = 2; break; case 'e': /* nop for compatibility */ break; case 'f': - co.do_force = 1; + g_co.do_force = 1; break; case 'h': /* help */ free(save_av); help(); break; /* NOTREACHED */ case 'i': - co.do_value_as_ip = 1; + g_co.do_value_as_ip = 1; break; case 'n': - co.test_only = 1; + g_co.test_only = 1; break; case 'N': - co.do_resolv = 1; + g_co.do_resolv = 1; break; case 'p': errx(EX_USAGE, "An absolute pathname must be used " "with -p option."); /* NOTREACHED */ case 'q': - co.do_quiet = 1; + g_co.do_quiet = 1; break; case 's': /* sort */ - co.do_sort = atoi(optarg); + g_co.do_sort = atoi(optarg); break; case 'S': - co.show_sets = 1; + g_co.show_sets = 1; break; case 't': - co.do_time = TIMESTAMP_STRING; + g_co.do_time = TIMESTAMP_STRING; break; case 'T': - co.do_time = TIMESTAMP_NUMERIC; + g_co.do_time = TIMESTAMP_NUMERIC; break; case 'v': /* verbose */ - co.verbose = 1; + g_co.verbose = 1; break; default: free(save_av); return 1; } ac -= optind; av += optind; NEED1("bad arguments, for usage summary ``ipfw''"); /* * An undocumented behaviour of ipfw1 was to allow rule numbers first, * e.g. "100 add allow ..." instead of "add 100 allow ...". * In case, swap first and second argument to get the normal form. */ if (ac > 1 && isdigit(*av[0])) { char *p = av[0]; av[0] = av[1]; av[1] = p; } /* * Optional: pipe, queue or nat. */ - co.do_nat = 0; - co.do_pipe = 0; - co.use_set = 0; + g_co.do_nat = 0; + g_co.do_pipe = 0; + g_co.use_set = 0; if (!strncmp(*av, "nat", strlen(*av))) - co.do_nat = 1; - else if (!strncmp(*av, "pipe", strlen(*av))) - co.do_pipe = 1; + g_co.do_nat = 1; + else if (!strncmp(*av, "pipe", strlen(*av))) + g_co.do_pipe = 1; else if (_substrcmp(*av, "queue") == 0) - co.do_pipe = 2; + g_co.do_pipe = 2; else if (_substrcmp(*av, "flowset") == 0) - co.do_pipe = 2; + g_co.do_pipe = 2; else if (_substrcmp(*av, "sched") == 0) - co.do_pipe = 3; + g_co.do_pipe = 3; else if (!strncmp(*av, "set", strlen(*av))) { if (ac > 1 && isdigit(av[1][0])) { - co.use_set = strtonum(av[1], 0, resvd_set_number, + g_co.use_set = strtonum(av[1], 0, resvd_set_number, &errstr); if (errstr) errx(EX_DATAERR, "invalid set number %s\n", av[1]); - ac -= 2; av += 2; co.use_set++; + ac -= 2; av += 2; g_co.use_set++; } } - if (co.do_pipe || co.do_nat) { + if (g_co.do_pipe || g_co.do_nat) { ac--; av++; } NEED1("missing command"); /* * For pipes, queues and nats we normally say 'nat|pipe NN config' * but the code is easier to parse as 'nat|pipe config NN' * so we swap the two arguments. */ - if ((co.do_pipe || co.do_nat) && ac > 1 && isdigit(*av[0])) { + if ((g_co.do_pipe || g_co.do_nat) && ac > 1 && isdigit(*av[0])) { char *p = av[0]; av[0] = av[1]; av[1] = p; } - if (co.use_set == 0) { + if (g_co.use_set == 0) { if (_substrcmp(*av, "add") == 0) ipfw_add(av); - else if (co.do_nat && _substrcmp(*av, "show") == 0) + else if (g_co.do_nat && _substrcmp(*av, "show") == 0) ipfw_show_nat(ac, av); - else if (co.do_pipe && _substrcmp(*av, "config") == 0) + else if (g_co.do_pipe && _substrcmp(*av, "config") == 0) ipfw_config_pipe(ac, av); - else if (co.do_nat && _substrcmp(*av, "config") == 0) + else if (g_co.do_nat && _substrcmp(*av, "config") == 0) ipfw_config_nat(ac, av); else if (_substrcmp(*av, "set") == 0) ipfw_sets_handler(av); else if (_substrcmp(*av, "table") == 0) ipfw_table_handler(ac, av); else if (_substrcmp(*av, "enable") == 0) ipfw_sysctl_handler(av, 1); else if (_substrcmp(*av, "disable") == 0) ipfw_sysctl_handler(av, 0); else try_next = 1; } - if (co.use_set || try_next) { + if (g_co.use_set || try_next) { if (_substrcmp(*av, "delete") == 0) ipfw_delete(av); else if (!strncmp(*av, "nat64clat", strlen(*av))) ipfw_nat64clat_handler(ac, av); else if (!strncmp(*av, "nat64stl", strlen(*av))) ipfw_nat64stl_handler(ac, av); else if (!strncmp(*av, "nat64lsn", strlen(*av))) ipfw_nat64lsn_handler(ac, av); else if (!strncmp(*av, "nptv6", strlen(*av))) ipfw_nptv6_handler(ac, av); else if (_substrcmp(*av, "flush") == 0) - ipfw_flush(co.do_force); + ipfw_flush(g_co.do_force); else if (_substrcmp(*av, "zero") == 0) ipfw_zero(ac, av, 0 /* IP_FW_ZERO */); else if (_substrcmp(*av, "resetlog") == 0) ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */); else if (_substrcmp(*av, "print") == 0 || _substrcmp(*av, "list") == 0) ipfw_list(ac, av, do_acct); else if (_substrcmp(*av, "show") == 0) ipfw_list(ac, av, 1 /* show counters */); else if (_substrcmp(*av, "table") == 0) ipfw_table_handler(ac, av); else if (_substrcmp(*av, "internal") == 0) ipfw_internal_handler(ac, av); else errx(EX_USAGE, "bad command `%s'", *av); } /* Free memory allocated in the argument parsing. */ free(save_av); return 0; } static void ipfw_readfile(int ac, char *av[]) { #define MAX_ARGS 32 char buf[4096]; char *progname = av[0]; /* original program name */ const char *cmd = NULL; /* preprocessor name, if any */ const char *filename = av[ac-1]; /* file to read */ int c, lineno=0; FILE *f = NULL; pid_t preproc = 0; while ((c = getopt(ac, av, "cfNnp:qS")) != -1) { switch(c) { case 'c': - co.do_compact = 1; + g_co.do_compact = 1; break; case 'f': - co.do_force = 1; + g_co.do_force = 1; break; case 'N': - co.do_resolv = 1; + g_co.do_resolv = 1; break; case 'n': - co.test_only = 1; + g_co.test_only = 1; break; case 'p': /* * ipfw -p cmd [args] filename * * We are done with getopt(). All arguments * except the filename go to the preprocessor, * so we need to do the following: * - check that a filename is actually present; * - advance av by optind-1 to skip arguments * already processed; * - decrease ac by optind, to remove the args * already processed and the final filename; * - set the last entry in av[] to NULL so * popen() can detect the end of the array; * - set optind=ac to let getopt() terminate. */ if (optind == ac) errx(EX_USAGE, "no filename argument"); cmd = optarg; av[ac-1] = NULL; av += optind - 1; ac -= optind; optind = ac; break; case 'q': - co.do_quiet = 1; + g_co.do_quiet = 1; break; case 'S': - co.show_sets = 1; + g_co.show_sets = 1; break; default: errx(EX_USAGE, "bad arguments, for usage" " summary ``ipfw''"); } } if (cmd == NULL && ac != optind + 1) errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]); if ((f = fopen(filename, "r")) == NULL) err(EX_UNAVAILABLE, "fopen: %s", filename); if (cmd != NULL) { /* pipe through preprocessor */ int pipedes[2]; if (pipe(pipedes) == -1) err(EX_OSERR, "cannot create pipe"); preproc = fork(); if (preproc == -1) err(EX_OSERR, "cannot fork"); if (preproc == 0) { /* * Child, will run the preprocessor with the * file on stdin and the pipe on stdout. */ if (dup2(fileno(f), 0) == -1 || dup2(pipedes[1], 1) == -1) err(EX_OSERR, "dup2()"); fclose(f); close(pipedes[1]); close(pipedes[0]); execvp(cmd, av); err(EX_OSERR, "execvp(%s) failed", cmd); } else { /* parent, will reopen f as the pipe */ fclose(f); close(pipedes[1]); if ((f = fdopen(pipedes[0], "r")) == NULL) { int savederrno = errno; (void)kill(preproc, SIGTERM); errno = savederrno; err(EX_OSERR, "fdopen()"); } } } while (fgets(buf, sizeof(buf), f)) { /* read commands */ char linename[20]; char *args[2]; lineno++; snprintf(linename, sizeof(linename), "Line %d", lineno); setprogname(linename); /* XXX */ args[0] = progname; args[1] = buf; ipfw_main(2, args); } fclose(f); if (cmd != NULL) { int status; if (waitpid(preproc, &status, 0) == -1) errx(EX_OSERR, "waitpid()"); if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) errx(EX_UNAVAILABLE, "preprocessor exited with status %d", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) errx(EX_UNAVAILABLE, "preprocessor exited with signal %d", WTERMSIG(status)); } } int main(int ac, char *av[]) { #if defined(_WIN32) && defined(TCC) { WSADATA wsaData; int ret=0; unsigned short wVersionRequested = MAKEWORD(2, 2); ret = WSAStartup(wVersionRequested, &wsaData); if (ret != 0) { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ printf("WSAStartup failed with error: %d\n", ret); return 1; } } #endif /* * If the last argument is an absolute pathname, interpret it * as a file to be preprocessed. */ if (ac > 1 && av[ac - 1][0] == '/') { if (access(av[ac - 1], R_OK) == 0) ipfw_readfile(ac, av); else err(EX_USAGE, "pathname: %s", av[ac - 1]); } else { if (ipfw_main(ac, av)) { errx(EX_USAGE, "usage: ipfw [options]\n" "do \"ipfw -h\" or \"man ipfw\" for details"); } } return EX_OK; } Index: head/sbin/ipfw/nat.c =================================================================== --- head/sbin/ipfw/nat.c (revision 363163) +++ head/sbin/ipfw/nat.c (revision 363164) @@ -1,1148 +1,1150 @@ /*- * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ * * In-kernel nat support */ #include #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include /* def. of struct route */ #include #include #include #include typedef int (nat_cb_t)(struct nat44_cfg_nat *cfg, void *arg); static void nat_show_cfg(struct nat44_cfg_nat *n, void *arg); static void nat_show_log(struct nat44_cfg_nat *n, void *arg); static int nat_show_data(struct nat44_cfg_nat *cfg, void *arg); static int natname_cmp(const void *a, const void *b); static int nat_foreach(nat_cb_t *f, void *arg, int sort); static int nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh); static struct _s_x nat_params[] = { { "ip", TOK_IP }, { "if", TOK_IF }, { "log", TOK_ALOG }, { "deny_in", TOK_DENY_INC }, { "same_ports", TOK_SAME_PORTS }, { "unreg_only", TOK_UNREG_ONLY }, { "unreg_cgn", TOK_UNREG_CGN }, { "skip_global", TOK_SKIP_GLOBAL }, { "reset", TOK_RESET_ADDR }, { "reverse", TOK_ALIAS_REV }, { "proxy_only", TOK_PROXY_ONLY }, { "redirect_addr", TOK_REDIR_ADDR }, { "redirect_port", TOK_REDIR_PORT }, { "redirect_proto", TOK_REDIR_PROTO }, { NULL, 0 } /* terminator */ }; /* * Search for interface with name "ifn", and fill n accordingly: * * n->ip ip address of interface "ifn" * n->if_name copy of interface name "ifn" */ static void set_addr_dynamic(const char *ifn, struct nat44_cfg_nat *n) { size_t needed; int mib[6]; char *buf, *lim, *next; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr_dl *sdl; struct sockaddr_in *sin; int ifIndex; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_IFLIST; mib[5] = 0; /* * Get interface data. */ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) err(1, "iflist-sysctl-estimate"); buf = safe_calloc(1, needed); if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) err(1, "iflist-sysctl-get"); lim = buf + needed; /* * Loop through interfaces until one with * given name is found. This is done to * find correct interface index for routing * message processing. */ ifIndex = 0; next = buf; while (next < lim) { ifm = (struct if_msghdr *)next; next += ifm->ifm_msglen; if (ifm->ifm_version != RTM_VERSION) { - if (co.verbose) + if (g_co.verbose) warnx("routing message version %d " "not understood", ifm->ifm_version); continue; } if (ifm->ifm_type == RTM_IFINFO) { sdl = (struct sockaddr_dl *)(ifm + 1); if (strlen(ifn) == sdl->sdl_nlen && strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { ifIndex = ifm->ifm_index; break; } } } if (!ifIndex) errx(1, "unknown interface name %s", ifn); /* * Get interface address. */ sin = NULL; while (next < lim) { ifam = (struct ifa_msghdr *)next; next += ifam->ifam_msglen; if (ifam->ifam_version != RTM_VERSION) { - if (co.verbose) + if (g_co.verbose) warnx("routing message version %d " "not understood", ifam->ifam_version); continue; } if (ifam->ifam_type != RTM_NEWADDR) break; if (ifam->ifam_addrs & RTA_IFA) { int i; char *cp = (char *)(ifam + 1); for (i = 1; i < RTA_IFA; i <<= 1) { if (ifam->ifam_addrs & i) cp += SA_SIZE((struct sockaddr *)cp); } if (((struct sockaddr *)cp)->sa_family == AF_INET) { sin = (struct sockaddr_in *)cp; break; } } } if (sin == NULL) n->ip.s_addr = htonl(INADDR_ANY); else n->ip = sin->sin_addr; strncpy(n->if_name, ifn, IF_NAMESIZE); free(buf); } /* * XXX - The following functions, macros and definitions come from natd.c: * it would be better to move them outside natd.c, in a file * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live * with it. */ /* * Definition of a port range, and macros to deal with values. * FORMAT: HI 16-bits == first port in range, 0 == all ports. * LO 16-bits == number of ports in range * NOTES: - Port values are not stored in network byte order. */ #define port_range u_long #define GETLOPORT(x) ((x) >> 0x10) #define GETNUMPORTS(x) ((x) & 0x0000ffff) #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) /* Set y to be the low-port value in port_range variable x. */ #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) /* Set y to be the number of ports in port_range variable x. */ #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) static void StrToAddr (const char* str, struct in_addr* addr) { struct hostent* hp; if (inet_aton (str, addr)) return; hp = gethostbyname (str); if (!hp) errx (1, "unknown host %s", str); memcpy (addr, hp->h_addr, sizeof (struct in_addr)); } static int StrToPortRange (const char* str, const char* proto, port_range *portRange) { char* sep; struct servent* sp; char* end; u_short loPort; u_short hiPort; /* First see if this is a service, return corresponding port if so. */ sp = getservbyname (str,proto); if (sp) { SETLOPORT(*portRange, ntohs(sp->s_port)); SETNUMPORTS(*portRange, 1); return 0; } /* Not a service, see if it's a single port or port range. */ sep = strchr (str, '-'); if (sep == NULL) { SETLOPORT(*portRange, strtol(str, &end, 10)); if (end != str) { /* Single port. */ SETNUMPORTS(*portRange, 1); return 0; } /* Error in port range field. */ errx (EX_DATAERR, "%s/%s: unknown service", str, proto); } /* Port range, get the values and sanity check. */ sscanf (str, "%hu-%hu", &loPort, &hiPort); SETLOPORT(*portRange, loPort); SETNUMPORTS(*portRange, 0); /* Error by default */ if (loPort <= hiPort) SETNUMPORTS(*portRange, hiPort - loPort + 1); if (GETNUMPORTS(*portRange) == 0) errx (EX_DATAERR, "invalid port range %s", str); return 0; } static int StrToProto (const char* str) { if (!strcmp (str, "tcp")) return IPPROTO_TCP; if (!strcmp (str, "udp")) return IPPROTO_UDP; if (!strcmp (str, "sctp")) return IPPROTO_SCTP; errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str); } static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) { char* ptr; ptr = strchr (str, ':'); if (!ptr) errx (EX_DATAERR, "%s is missing port number", str); *ptr = '\0'; ++ptr; StrToAddr (str, addr); return StrToPortRange (ptr, proto, portRange); } /* End of stuff taken from natd.c. */ /* * The next 3 functions add support for the addr, port and proto redirect and * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect() * and SetupProtoRedirect() from natd.c. * * Every setup_* function fills at least one redirect entry * (struct nat44_cfg_redir) and zero or more server pool entry * (struct nat44_cfg_spool) in buf. * * The format of data in buf is: * * nat44_cfg_nat nat44_cfg_redir nat44_cfg_spool ...... nat44_cfg_spool * * ------------------------------------- ------------ * | | .....X ..... | | | | ..... * ------------------------------------- ...... ------------ * ^ * spool_cnt n=0 ...... n=(X-1) * * len points to the amount of available space in buf * space counts the memory consumed by every function * * XXX - Every function get all the argv params so it * has to check, in optional parameters, that the next * args is a valid option for the redir entry and not * another token. Only redir_port and redir_proto are * affected by this. */ static int estimate_redir_addr(int *ac, char ***av) { size_t space = sizeof(struct nat44_cfg_redir); char *sep = **av; u_int c = 0; (void)ac; /* UNUSED */ while ((sep = strchr(sep, ',')) != NULL) { c++; sep++; } if (c > 0) c++; space += c * sizeof(struct nat44_cfg_spool); return (space); } static int setup_redir_addr(char *buf, int *ac, char ***av) { struct nat44_cfg_redir *r; char *sep; size_t space; r = (struct nat44_cfg_redir *)buf; r->mode = REDIR_ADDR; /* Skip nat44_cfg_redir at beginning of buf. */ buf = &buf[sizeof(struct nat44_cfg_redir)]; space = sizeof(struct nat44_cfg_redir); /* Extract local address. */ if (strchr(**av, ',') != NULL) { struct nat44_cfg_spool *spool; /* Setup LSNAT server pool. */ r->laddr.s_addr = INADDR_NONE; sep = strtok(**av, ","); while (sep != NULL) { spool = (struct nat44_cfg_spool *)buf; space += sizeof(struct nat44_cfg_spool); StrToAddr(sep, &spool->addr); spool->port = ~0; r->spool_cnt++; /* Point to the next possible nat44_cfg_spool. */ buf = &buf[sizeof(struct nat44_cfg_spool)]; sep = strtok(NULL, ","); } } else StrToAddr(**av, &r->laddr); (*av)++; (*ac)--; /* Extract public address. */ StrToAddr(**av, &r->paddr); (*av)++; (*ac)--; return (space); } static int estimate_redir_port(int *ac, char ***av) { size_t space = sizeof(struct nat44_cfg_redir); char *sep = **av; u_int c = 0; (void)ac; /* UNUSED */ while ((sep = strchr(sep, ',')) != NULL) { c++; sep++; } if (c > 0) c++; space += c * sizeof(struct nat44_cfg_spool); return (space); } static int setup_redir_port(char *buf, int *ac, char ***av) { struct nat44_cfg_redir *r; char *sep, *protoName, *lsnat = NULL; size_t space; u_short numLocalPorts; port_range portRange; numLocalPorts = 0; r = (struct nat44_cfg_redir *)buf; r->mode = REDIR_PORT; /* Skip nat44_cfg_redir at beginning of buf. */ buf = &buf[sizeof(struct nat44_cfg_redir)]; space = sizeof(struct nat44_cfg_redir); /* * Extract protocol. */ r->proto = StrToProto(**av); protoName = **av; (*av)++; (*ac)--; /* * Extract local address. */ if (strchr(**av, ',') != NULL) { r->laddr.s_addr = INADDR_NONE; r->lport = ~0; numLocalPorts = 1; lsnat = **av; } else { /* * The sctp nat does not allow the port numbers to be mapped to * new port numbers. Therefore, no ports are to be specified * in the target port field. */ if (r->proto == IPPROTO_SCTP) { if (strchr(**av, ':')) errx(EX_DATAERR, "redirect_port:" "port numbers do not change in sctp, so do " "not specify them as part of the target"); else StrToAddr(**av, &r->laddr); } else { if (StrToAddrAndPortRange(**av, &r->laddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " "invalid local port range"); r->lport = GETLOPORT(portRange); numLocalPorts = GETNUMPORTS(portRange); } } (*av)++; (*ac)--; /* * Extract public port and optionally address. */ if (strchr(**av, ':') != NULL) { if (StrToAddrAndPortRange(**av, &r->paddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " "invalid public port range"); } else { r->paddr.s_addr = INADDR_ANY; if (StrToPortRange(**av, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " "invalid public port range"); } r->pport = GETLOPORT(portRange); if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */ numLocalPorts = GETNUMPORTS(portRange); r->lport = r->pport; } r->pport_cnt = GETNUMPORTS(portRange); (*av)++; (*ac)--; /* * Extract remote address and optionally port. */ /* * NB: isdigit(**av) => we've to check that next parameter is really an * option for this redirect entry, else stop here processing arg[cv]. */ if (*ac != 0 && isdigit(***av)) { if (strchr(**av, ':') != NULL) { if (StrToAddrAndPortRange(**av, &r->raddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " "invalid remote port range"); } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); StrToAddr(**av, &r->raddr); } (*av)++; (*ac)--; } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); r->raddr.s_addr = INADDR_ANY; } r->rport = GETLOPORT(portRange); r->rport_cnt = GETNUMPORTS(portRange); /* * Make sure port ranges match up, then add the redirect ports. */ if (numLocalPorts != r->pport_cnt) errx(EX_DATAERR, "redirect_port: " "port ranges must be equal in size"); /* Remote port range is allowed to be '0' which means all ports. */ if (r->rport_cnt != numLocalPorts && (r->rport_cnt != 1 || r->rport != 0)) errx(EX_DATAERR, "redirect_port: remote port must" "be 0 or equal to local port range in size"); /* Setup LSNAT server pool. */ if (lsnat != NULL) { struct nat44_cfg_spool *spool; sep = strtok(lsnat, ","); while (sep != NULL) { spool = (struct nat44_cfg_spool *)buf; space += sizeof(struct nat44_cfg_spool); /* * The sctp nat does not allow the port numbers to * be mapped to new port numbers. Therefore, no ports * are to be specified in the target port field. */ if (r->proto == IPPROTO_SCTP) { if (strchr (sep, ':')) { errx(EX_DATAERR, "redirect_port:" "port numbers do not change in " "sctp, so do not specify them as " "part of the target"); } else { StrToAddr(sep, &spool->addr); spool->port = r->pport; } } else { if (StrToAddrAndPortRange(sep, &spool->addr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port:" "invalid local port range"); if (GETNUMPORTS(portRange) != 1) errx(EX_DATAERR, "redirect_port: " "local port must be single in " "this context"); spool->port = GETLOPORT(portRange); } r->spool_cnt++; /* Point to the next possible nat44_cfg_spool. */ buf = &buf[sizeof(struct nat44_cfg_spool)]; sep = strtok(NULL, ","); } } return (space); } static int setup_redir_proto(char *buf, int *ac, char ***av) { struct nat44_cfg_redir *r; struct protoent *protoent; size_t space; r = (struct nat44_cfg_redir *)buf; r->mode = REDIR_PROTO; /* Skip nat44_cfg_redir at beginning of buf. */ buf = &buf[sizeof(struct nat44_cfg_redir)]; space = sizeof(struct nat44_cfg_redir); /* * Extract protocol. */ protoent = getprotobyname(**av); if (protoent == NULL) errx(EX_DATAERR, "redirect_proto: unknown protocol %s", **av); else r->proto = protoent->p_proto; (*av)++; (*ac)--; /* * Extract local address. */ StrToAddr(**av, &r->laddr); (*av)++; (*ac)--; /* * Extract optional public address. */ if (*ac == 0) { r->paddr.s_addr = INADDR_ANY; r->raddr.s_addr = INADDR_ANY; } else { /* see above in setup_redir_port() */ if (isdigit(***av)) { StrToAddr(**av, &r->paddr); (*av)++; (*ac)--; /* * Extract optional remote address. */ /* see above in setup_redir_port() */ if (*ac != 0 && isdigit(***av)) { StrToAddr(**av, &r->raddr); (*av)++; (*ac)--; } } } return (space); } static void -nat_show_log(struct nat44_cfg_nat *n, void *arg) +nat_show_log(struct nat44_cfg_nat *n, void *arg __unused) { char *buf; buf = (char *)(n + 1); if (buf[0] != '\0') printf("nat %s: %s\n", n->name, buf); } static void -nat_show_cfg(struct nat44_cfg_nat *n, void *arg) +nat_show_cfg(struct nat44_cfg_nat *n, void *arg __unused) { - int i, cnt, off; struct nat44_cfg_redir *t; struct nat44_cfg_spool *s; caddr_t buf; struct protoent *p; + uint32_t cnt; + int i, off; buf = (caddr_t)n; off = sizeof(*n); printf("ipfw nat %s config", n->name); if (strlen(n->if_name) != 0) printf(" if %s", n->if_name); else if (n->ip.s_addr != 0) printf(" ip %s", inet_ntoa(n->ip)); while (n->mode != 0) { if (n->mode & PKT_ALIAS_LOG) { printf(" log"); n->mode &= ~PKT_ALIAS_LOG; } else if (n->mode & PKT_ALIAS_DENY_INCOMING) { printf(" deny_in"); n->mode &= ~PKT_ALIAS_DENY_INCOMING; } else if (n->mode & PKT_ALIAS_SAME_PORTS) { printf(" same_ports"); n->mode &= ~PKT_ALIAS_SAME_PORTS; } else if (n->mode & PKT_ALIAS_SKIP_GLOBAL) { printf(" skip_global"); n->mode &= ~PKT_ALIAS_SKIP_GLOBAL; } else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) { printf(" unreg_only"); n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY; } else if (n->mode & PKT_ALIAS_UNREGISTERED_CGN) { printf(" unreg_cgn"); n->mode &= ~PKT_ALIAS_UNREGISTERED_CGN; } else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) { printf(" reset"); n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE; } else if (n->mode & PKT_ALIAS_REVERSE) { printf(" reverse"); n->mode &= ~PKT_ALIAS_REVERSE; } else if (n->mode & PKT_ALIAS_PROXY_ONLY) { printf(" proxy_only"); n->mode &= ~PKT_ALIAS_PROXY_ONLY; } } /* Print all the redirect's data configuration. */ for (cnt = 0; cnt < n->redir_cnt; cnt++) { t = (struct nat44_cfg_redir *)&buf[off]; off += sizeof(struct nat44_cfg_redir); switch (t->mode) { case REDIR_ADDR: printf(" redirect_addr"); if (t->spool_cnt == 0) printf(" %s", inet_ntoa(t->laddr)); else for (i = 0; i < t->spool_cnt; i++) { s = (struct nat44_cfg_spool *)&buf[off]; if (i) printf(","); else printf(" "); printf("%s", inet_ntoa(s->addr)); off += sizeof(struct nat44_cfg_spool); } printf(" %s", inet_ntoa(t->paddr)); break; case REDIR_PORT: p = getprotobynumber(t->proto); printf(" redirect_port %s ", p->p_name); if (!t->spool_cnt) { printf("%s:%u", inet_ntoa(t->laddr), t->lport); if (t->pport_cnt > 1) printf("-%u", t->lport + t->pport_cnt - 1); } else for (i=0; i < t->spool_cnt; i++) { s = (struct nat44_cfg_spool *)&buf[off]; if (i) printf(","); printf("%s:%u", inet_ntoa(s->addr), s->port); off += sizeof(struct nat44_cfg_spool); } printf(" "); if (t->paddr.s_addr) printf("%s:", inet_ntoa(t->paddr)); printf("%u", t->pport); if (!t->spool_cnt && t->pport_cnt > 1) printf("-%u", t->pport + t->pport_cnt - 1); if (t->raddr.s_addr) { printf(" %s", inet_ntoa(t->raddr)); if (t->rport) { printf(":%u", t->rport); if (!t->spool_cnt && t->rport_cnt > 1) printf("-%u", t->rport + t->rport_cnt - 1); } } break; case REDIR_PROTO: p = getprotobynumber(t->proto); printf(" redirect_proto %s %s", p->p_name, inet_ntoa(t->laddr)); if (t->paddr.s_addr != 0) { printf(" %s", inet_ntoa(t->paddr)); if (t->raddr.s_addr) printf(" %s", inet_ntoa(t->raddr)); } break; default: errx(EX_DATAERR, "unknown redir mode"); break; } } printf("\n"); } void ipfw_config_nat(int ac, char **av) { ipfw_obj_header *oh; struct nat44_cfg_nat *n; /* Nat instance configuration. */ int i, off, tok, ac1; char *id, *buf, **av1, *end; size_t len; av++; ac--; /* Nat id. */ if (ac == 0) errx(EX_DATAERR, "missing nat id"); id = *av; i = (int)strtol(id, &end, 0); if (i <= 0 || *end != '\0') errx(EX_DATAERR, "illegal nat id: %s", id); av++; ac--; if (ac == 0) errx(EX_DATAERR, "missing option"); len = sizeof(*oh) + sizeof(*n); ac1 = ac; av1 = av; while (ac1 > 0) { tok = match_token(nat_params, *av1); ac1--; av1++; switch (tok) { case TOK_IP: case TOK_IF: ac1--; av1++; break; case TOK_ALOG: case TOK_DENY_INC: case TOK_SAME_PORTS: case TOK_SKIP_GLOBAL: case TOK_UNREG_ONLY: case TOK_UNREG_CGN: case TOK_RESET_ADDR: case TOK_ALIAS_REV: case TOK_PROXY_ONLY: break; case TOK_REDIR_ADDR: if (ac1 < 2) errx(EX_DATAERR, "redirect_addr: " "not enough arguments"); len += estimate_redir_addr(&ac1, &av1); av1 += 2; ac1 -= 2; break; case TOK_REDIR_PORT: if (ac1 < 3) errx(EX_DATAERR, "redirect_port: " "not enough arguments"); av1++; ac1--; len += estimate_redir_port(&ac1, &av1); av1 += 2; ac1 -= 2; /* Skip optional remoteIP/port */ if (ac1 != 0 && isdigit(**av1)) { av1++; ac1--; } break; case TOK_REDIR_PROTO: if (ac1 < 2) errx(EX_DATAERR, "redirect_proto: " "not enough arguments"); len += sizeof(struct nat44_cfg_redir); av1 += 2; ac1 -= 2; /* Skip optional remoteIP/port */ if (ac1 != 0 && isdigit(**av1)) { av1++; ac1--; } if (ac1 != 0 && isdigit(**av1)) { av1++; ac1--; } break; default: errx(EX_DATAERR, "unrecognised option ``%s''", av1[-1]); } } if ((buf = malloc(len)) == NULL) errx(EX_OSERR, "malloc failed"); /* Offset in buf: save space for header at the beginning. */ off = sizeof(*oh) + sizeof(*n); memset(buf, 0, len); oh = (ipfw_obj_header *)buf; n = (struct nat44_cfg_nat *)(oh + 1); oh->ntlv.head.length = sizeof(oh->ntlv); snprintf(oh->ntlv.name, sizeof(oh->ntlv.name), "%d", i); snprintf(n->name, sizeof(n->name), "%d", i); while (ac > 0) { tok = match_token(nat_params, *av); ac--; av++; switch (tok) { case TOK_IP: if (ac == 0) errx(EX_DATAERR, "missing option"); if (!inet_aton(av[0], &(n->ip))) errx(EX_DATAERR, "bad ip address ``%s''", av[0]); ac--; av++; break; case TOK_IF: if (ac == 0) errx(EX_DATAERR, "missing option"); set_addr_dynamic(av[0], n); ac--; av++; break; case TOK_ALOG: n->mode |= PKT_ALIAS_LOG; break; case TOK_DENY_INC: n->mode |= PKT_ALIAS_DENY_INCOMING; break; case TOK_SAME_PORTS: n->mode |= PKT_ALIAS_SAME_PORTS; break; case TOK_UNREG_ONLY: n->mode |= PKT_ALIAS_UNREGISTERED_ONLY; break; case TOK_UNREG_CGN: n->mode |= PKT_ALIAS_UNREGISTERED_CGN; break; case TOK_SKIP_GLOBAL: n->mode |= PKT_ALIAS_SKIP_GLOBAL; break; case TOK_RESET_ADDR: n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; break; case TOK_ALIAS_REV: n->mode |= PKT_ALIAS_REVERSE; break; case TOK_PROXY_ONLY: n->mode |= PKT_ALIAS_PROXY_ONLY; break; /* * All the setup_redir_* functions work directly in * the final buffer, see above for details. */ case TOK_REDIR_ADDR: case TOK_REDIR_PORT: case TOK_REDIR_PROTO: switch (tok) { case TOK_REDIR_ADDR: i = setup_redir_addr(&buf[off], &ac, &av); break; case TOK_REDIR_PORT: i = setup_redir_port(&buf[off], &ac, &av); break; case TOK_REDIR_PROTO: i = setup_redir_proto(&buf[off], &ac, &av); break; } n->redir_cnt++; off += i; break; } } i = do_set3(IP_FW_NAT44_XCONFIG, &oh->opheader, len); if (i != 0) err(1, "setsockopt(%s)", "IP_FW_NAT44_XCONFIG"); - if (!co.do_quiet) { + if (!g_co.do_quiet) { /* After every modification, we show the resultant rule. */ int _ac = 3; const char *_av[] = {"show", "config", id}; ipfw_show_nat(_ac, (char **)(void *)_av); } } static void nat_fill_ntlv(ipfw_obj_ntlv *ntlv, int i) { ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = 1; ntlv->set = 0; /* not yet */ snprintf(ntlv->name, sizeof(ntlv->name), "%d", i); } int ipfw_delete_nat(int i) { ipfw_obj_header oh; int ret; memset(&oh, 0, sizeof(oh)); nat_fill_ntlv(&oh.ntlv, i); ret = do_set3(IP_FW_NAT44_DESTROY, &oh.opheader, sizeof(oh)); if (ret == -1) { - if (!co.do_quiet) + if (!g_co.do_quiet) warn("nat %u not available", i); return (EX_UNAVAILABLE); } return (EX_OK); } struct nat_list_arg { uint16_t cmd; int is_all; }; static int nat_show_data(struct nat44_cfg_nat *cfg, void *arg) { struct nat_list_arg *nla; ipfw_obj_header *oh; nla = (struct nat_list_arg *)arg; switch (nla->cmd) { case IP_FW_NAT44_XGETCONFIG: if (nat_get_cmd(cfg->name, nla->cmd, &oh) != 0) { warnx("Error getting nat instance %s info", cfg->name); break; } nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL); free(oh); break; case IP_FW_NAT44_XGETLOG: if (nat_get_cmd(cfg->name, nla->cmd, &oh) == 0) { nat_show_log((struct nat44_cfg_nat *)(oh + 1), NULL); free(oh); break; } /* Handle error */ if (nla->is_all != 0 && errno == ENOENT) break; warn("Error getting nat instance %s info", cfg->name); break; } return (0); } /* * Compare nat names. * Honor number comparison. */ static int natname_cmp(const void *a, const void *b) { - struct nat44_cfg_nat *ia, *ib; + const struct nat44_cfg_nat *ia, *ib; - ia = (struct nat44_cfg_nat *)a; - ib = (struct nat44_cfg_nat *)b; + ia = (const struct nat44_cfg_nat *)a; + ib = (const struct nat44_cfg_nat *)b; return (stringnum_cmp(ia->name, ib->name)); } /* * Retrieves nat list from kernel, * optionally sorts it and calls requested function for each table. * Returns 0 on success. */ static int nat_foreach(nat_cb_t *f, void *arg, int sort) { ipfw_obj_lheader *olh; struct nat44_cfg_nat *cfg; size_t sz; - int i, error; + uint32_t i; + int error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(struct nat44_cfg_nat); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_NAT44_LIST_NAT, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno == ENOMEM) continue; return (errno); } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, natname_cmp); cfg = (struct nat44_cfg_nat*)(olh + 1); for (i = 0; i < olh->count; i++) { error = f(cfg, arg); /* Ignore errors for now */ cfg = (struct nat44_cfg_nat *)((caddr_t)cfg + olh->objsize); } free(olh); break; } return (0); } static int nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh) { ipfw_obj_header *oh; struct nat44_cfg_nat *cfg; size_t sz; /* Start with reasonable default */ sz = sizeof(*oh) + sizeof(*cfg) + 128; for (;;) { if ((oh = calloc(1, sz)) == NULL) return (ENOMEM); cfg = (struct nat44_cfg_nat *)(oh + 1); oh->ntlv.head.length = sizeof(oh->ntlv); strlcpy(oh->ntlv.name, name, sizeof(oh->ntlv.name)); strlcpy(cfg->name, name, sizeof(cfg->name)); if (do_get3(cmd, &oh->opheader, &sz) != 0) { sz = cfg->size; free(oh); if (errno == ENOMEM) continue; return (errno); } *ooh = oh; break; } return (0); } void ipfw_show_nat(int ac, char **av) { ipfw_obj_header *oh; char *name; int cmd; struct nat_list_arg nla; ac--; av++; - if (co.test_only) + if (g_co.test_only) return; /* Parse parameters. */ cmd = 0; /* XXX: Change to IP_FW_NAT44_XGETLOG @ MFC */ name = NULL; for ( ; ac != 0; ac--, av++) { if (!strncmp(av[0], "config", strlen(av[0]))) { cmd = IP_FW_NAT44_XGETCONFIG; continue; } if (strcmp(av[0], "log") == 0) { cmd = IP_FW_NAT44_XGETLOG; continue; } if (name != NULL) err(EX_USAGE,"only one instance name may be specified"); name = av[0]; } if (cmd == 0) errx(EX_USAGE, "Please specify action. Available: config,log"); if (name == NULL) { memset(&nla, 0, sizeof(nla)); nla.cmd = cmd; nla.is_all = 1; nat_foreach(nat_show_data, &nla, 1); } else { if (nat_get_cmd(name, cmd, &oh) != 0) err(EX_OSERR, "Error getting nat %s instance info", name); nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL); free(oh); } } Index: head/sbin/ipfw/nat64clat.c =================================================================== --- head/sbin/ipfw/nat64clat.c (revision 363163) +++ head/sbin/ipfw/nat64clat.c (revision 363164) @@ -1,535 +1,537 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Yandex LLC * Copyright (c) 2019 Andrey V. Elsukov * Copyright (c) 2019 Boris N. Lytochkin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef int (nat64clat_cb_t)(ipfw_nat64clat_cfg *i, const char *name, uint8_t set); static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort); static void nat64clat_create(const char *name, uint8_t set, int ac, char **av); static void nat64clat_config(const char *name, uint8_t set, int ac, char **av); static void nat64clat_destroy(const char *name, uint8_t set); static void nat64clat_stats(const char *name, uint8_t set); static void nat64clat_reset_stats(const char *name, uint8_t set); static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set); static int nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set); static struct _s_x nat64cmds[] = { { "create", TOK_CREATE }, { "config", TOK_CONFIG }, { "destroy", TOK_DESTROY }, { "list", TOK_LIST }, { "show", TOK_LIST }, { "stats", TOK_STATS }, { NULL, 0 } }; static struct _s_x nat64statscmds[] = { { "reset", TOK_RESET }, { NULL, 0 } }; /* * This one handles all nat64clat-related commands * ipfw [set N] nat64clat NAME {create | config} ... * ipfw [set N] nat64clat NAME stats [reset] * ipfw [set N] nat64clat {NAME | all} destroy * ipfw [set N] nat64clat {NAME | all} {list | show} */ #define nat64clat_check_name table_check_name void ipfw_nat64clat_handler(int ac, char *av[]) { const char *name; int tcmd; uint8_t set; - if (co.use_set != 0) - set = co.use_set - 1; + if (g_co.use_set != 0) + set = g_co.use_set - 1; else set = 0; ac--; av++; NEED1("nat64clat needs instance name"); name = *av; if (nat64clat_check_name(name) != 0) { if (strcmp(name, "all") == 0) name = NULL; else errx(EX_USAGE, "nat64clat instance name %s is invalid", name); } ac--; av++; NEED1("nat64clat needs command"); tcmd = get_token(nat64cmds, *av, "nat64clat command"); if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) errx(EX_USAGE, "nat64clat instance name required"); switch (tcmd) { case TOK_CREATE: ac--; av++; nat64clat_create(name, set, ac, av); break; case TOK_CONFIG: ac--; av++; nat64clat_config(name, set, ac, av); break; case TOK_LIST: nat64clat_foreach(nat64clat_show_cb, name, set, 1); break; case TOK_DESTROY: if (name == NULL) nat64clat_foreach(nat64clat_destroy_cb, NULL, set, 0); else nat64clat_destroy(name, set); break; case TOK_STATS: ac--; av++; if (ac == 0) { nat64clat_stats(name, set); break; } tcmd = get_token(nat64statscmds, *av, "stats command"); if (tcmd == TOK_RESET) nat64clat_reset_stats(name, set); } } static void nat64clat_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) { ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = 1; ntlv->set = set; strlcpy(ntlv->name, name, sizeof(ntlv->name)); } static struct _s_x nat64newcmds[] = { { "plat_prefix", TOK_PLAT_PREFIX }, { "clat_prefix", TOK_CLAT_PREFIX }, { "log", TOK_LOG }, { "-log", TOK_LOGOFF }, { "allow_private", TOK_PRIVATE }, { "-allow_private", TOK_PRIVATEOFF }, { NULL, 0 } }; /* * Creates new nat64clat instance * ipfw nat64clat create clat_prefix plat_prefix * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ] */ #define NAT64CLAT_HAS_CLAT_PREFIX 0x01 #define NAT64CLAT_HAS_PLAT_PREFIX 0x02 static void nat64clat_create(const char *name, uint8_t set, int ac, char *av[]) { char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64clat_cfg)]; ipfw_nat64clat_cfg *cfg; ipfw_obj_lheader *olh; int tcmd, flags; char *p; struct in6_addr prefix; uint8_t plen; memset(buf, 0, sizeof(buf)); olh = (ipfw_obj_lheader *)buf; cfg = (ipfw_nat64clat_cfg *)(olh + 1); /* Some reasonable defaults */ inet_pton(AF_INET6, "64:ff9b::", &cfg->plat_prefix); cfg->plat_plen = 96; cfg->set = set; flags = NAT64CLAT_HAS_PLAT_PREFIX; while (ac > 0) { tcmd = get_token(nat64newcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_PLAT_PREFIX: case TOK_CLAT_PREFIX: if (tcmd == TOK_PLAT_PREFIX) { NEED1("IPv6 plat_prefix required"); } else { NEED1("IPv6 clat_prefix required"); } if ((p = strchr(*av, '/')) != NULL) *p++ = '\0'; if (inet_pton(AF_INET6, *av, &prefix) != 1) errx(EX_USAGE, "Bad prefix: %s", *av); plen = strtol(p, NULL, 10); if (ipfw_check_nat64prefix(&prefix, plen) != 0) errx(EX_USAGE, "Bad prefix length: %s", p); if (tcmd == TOK_PLAT_PREFIX) { flags |= NAT64CLAT_HAS_PLAT_PREFIX; cfg->plat_prefix = prefix; cfg->plat_plen = plen; } else { flags |= NAT64CLAT_HAS_CLAT_PREFIX; cfg->clat_prefix = prefix; cfg->clat_plen = plen; } ac--; av++; break; case TOK_LOG: cfg->flags |= NAT64_LOG; break; case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; case TOK_PRIVATE: cfg->flags |= NAT64_ALLOW_PRIVATE; break; case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; } } /* Check validness */ if ((flags & NAT64CLAT_HAS_PLAT_PREFIX) != NAT64CLAT_HAS_PLAT_PREFIX) errx(EX_USAGE, "plat_prefix required"); if ((flags & NAT64CLAT_HAS_CLAT_PREFIX) != NAT64CLAT_HAS_CLAT_PREFIX) errx(EX_USAGE, "clat_prefix required"); olh->count = 1; olh->objsize = sizeof(*cfg); olh->size = sizeof(buf); strlcpy(cfg->name, name, sizeof(cfg->name)); if (do_set3(IP_FW_NAT64CLAT_CREATE, &olh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nat64clat instance creation failed"); } /* * Configures existing nat64clat instance * ipfw nat64clat config * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ] */ static void nat64clat_config(const char *name, uint8_t set, int ac, char **av) { char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64clat_cfg)]; ipfw_nat64clat_cfg *cfg; ipfw_obj_header *oh; char *opt; char *p; size_t sz; int tcmd; struct in6_addr prefix; uint8_t plen; if (ac == 0) errx(EX_USAGE, "config options required"); memset(&buf, 0, sizeof(buf)); oh = (ipfw_obj_header *)buf; cfg = (ipfw_nat64clat_cfg *)(oh + 1); sz = sizeof(buf); nat64clat_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, &sz) != 0) err(EX_OSERR, "failed to get config for instance %s", name); while (ac > 0) { tcmd = get_token(nat64newcmds, *av, "option"); opt = *av; ac--; av++; switch (tcmd) { case TOK_PLAT_PREFIX: case TOK_CLAT_PREFIX: if (tcmd == TOK_PLAT_PREFIX) { NEED1("IPv6 plat_prefix required"); } else { NEED1("IPv6 clat_prefix required"); } if ((p = strchr(*av, '/')) != NULL) *p++ = '\0'; if (inet_pton(AF_INET6, *av, &prefix) != 1) errx(EX_USAGE, "Bad prefix: %s", *av); plen = strtol(p, NULL, 10); if (ipfw_check_nat64prefix(&prefix, plen) != 0) errx(EX_USAGE, "Bad prefix length: %s", p); if (tcmd == TOK_PLAT_PREFIX) { cfg->plat_prefix = prefix; cfg->plat_plen = plen; } else { cfg->clat_prefix = prefix; cfg->clat_plen = plen; } ac--; av++; break; case TOK_LOG: cfg->flags |= NAT64_LOG; break; case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; case TOK_PRIVATE: cfg->flags |= NAT64_ALLOW_PRIVATE; break; case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; default: errx(EX_USAGE, "Can't change %s option", opt); } } if (do_set3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nat64clat instance configuration failed"); } /* * Destroys nat64clat instance. * Request: [ ipfw_obj_header ] */ static void nat64clat_destroy(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nat64clat_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NAT64CLAT_DESTROY, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to destroy nat instance %s", name); } /* * Get nat64clat instance statistics. * Request: [ ipfw_obj_header ] * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] */ static int nat64clat_get_stats(const char *name, uint8_t set, struct ipfw_nat64clat_stats *stats) { ipfw_obj_header *oh; ipfw_obj_ctlv *oc; size_t sz; sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); oh = calloc(1, sz); nat64clat_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NAT64CLAT_STATS, &oh->opheader, &sz) == 0) { oc = (ipfw_obj_ctlv *)(oh + 1); memcpy(stats, oc + 1, sizeof(*stats)); free(oh); return (0); } free(oh); return (-1); } static void nat64clat_stats(const char *name, uint8_t set) { struct ipfw_nat64clat_stats stats; if (nat64clat_get_stats(name, set, &stats) != 0) err(EX_OSERR, "Error retrieving stats"); - if (co.use_set != 0 || set != 0) + if (g_co.use_set != 0 || set != 0) printf("set %u ", set); printf("nat64clat %s\n", name); printf("\t%ju packets translated from IPv6 to IPv4\n", (uintmax_t)stats.opcnt64); printf("\t%ju packets translated from IPv4 to IPv6\n", (uintmax_t)stats.opcnt46); printf("\t%ju IPv6 fragments created\n", (uintmax_t)stats.ofrags); printf("\t%ju IPv4 fragments received\n", (uintmax_t)stats.ifrags); printf("\t%ju output packets dropped due to no bufs, etc.\n", (uintmax_t)stats.oerrors); printf("\t%ju output packets discarded due to no IPv4 route\n", (uintmax_t)stats.noroute4); printf("\t%ju output packets discarded due to no IPv6 route\n", (uintmax_t)stats.noroute6); printf("\t%ju packets discarded due to unsupported protocol\n", (uintmax_t)stats.noproto); printf("\t%ju packets discarded due to memory allocation problems\n", (uintmax_t)stats.nomem); printf("\t%ju packets discarded due to some errors\n", (uintmax_t)stats.dropped); } /* * Reset nat64clat instance statistics specified by @oh->ntlv. * Request: [ ipfw_obj_header ] */ static void nat64clat_reset_stats(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nat64clat_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NAT64CLAT_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to reset stats for instance %s", name); } static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set) { char plat_buf[INET6_ADDRSTRLEN], clat_buf[INET6_ADDRSTRLEN]; if (name != NULL && strcmp(cfg->name, name) != 0) return (ESRCH); - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); - if (co.use_set != 0 || cfg->set != 0) + if (g_co.use_set != 0 || cfg->set != 0) printf("set %u ", cfg->set); inet_ntop(AF_INET6, &cfg->clat_prefix, clat_buf, sizeof(clat_buf)); inet_ntop(AF_INET6, &cfg->plat_prefix, plat_buf, sizeof(plat_buf)); printf("nat64clat %s clat_prefix %s/%u plat_prefix %s/%u", cfg->name, clat_buf, cfg->clat_plen, plat_buf, cfg->plat_plen); if (cfg->flags & NAT64_LOG) printf(" log"); if (cfg->flags & NAT64_ALLOW_PRIVATE) printf(" allow_private"); printf("\n"); return (0); } static int -nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set) +nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name __unused, + uint8_t set) { - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); nat64clat_destroy(cfg->name, cfg->set); return (0); } /* * Compare nat64clat instances names. * Honor number comparison. */ static int nat64name_cmp(const void *a, const void *b) { - ipfw_nat64clat_cfg *ca, *cb; + const ipfw_nat64clat_cfg *ca, *cb; - ca = (ipfw_nat64clat_cfg *)a; - cb = (ipfw_nat64clat_cfg *)b; + ca = (const ipfw_nat64clat_cfg *)a; + cb = (const ipfw_nat64clat_cfg *)b; if (ca->set > cb->set) return (1); else if (ca->set < cb->set) return (-1); return (stringnum_cmp(ca->name, cb->name)); } /* * Retrieves nat64clat instance list from kernel, * optionally sorts it and calls requested function for each instance. * * Request: [ ipfw_obj_lheader ] * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ] */ static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort) { ipfw_obj_lheader *olh; ipfw_nat64clat_cfg *cfg; size_t sz; - int i, error; + uint32_t i; + int error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(*cfg); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_NAT64CLAT_LIST, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno != ENOMEM) return (errno); continue; } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, nat64name_cmp); cfg = (ipfw_nat64clat_cfg *)(olh + 1); for (i = 0; i < olh->count; i++) { error = f(cfg, name, set); /* Ignore errors for now */ cfg = (ipfw_nat64clat_cfg *)((caddr_t)cfg + olh->objsize); } free(olh); break; } return (0); } Index: head/sbin/ipfw/nat64lsn.c =================================================================== --- head/sbin/ipfw/nat64lsn.c (revision 363163) +++ head/sbin/ipfw/nat64lsn.c (revision 363164) @@ -1,884 +1,888 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2015-2019 Yandex LLC * Copyright (c) 2015-2016 Alexander V. Chernikov * Copyright (c) 2015-2019 Andrey V. Elsukov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void nat64lsn_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set); typedef int (nat64lsn_cb_t)(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set); static int nat64lsn_foreach(nat64lsn_cb_t *f, const char *name, uint8_t set, int sort); static void nat64lsn_create(const char *name, uint8_t set, int ac, char **av); static void nat64lsn_config(const char *name, uint8_t set, int ac, char **av); static void nat64lsn_destroy(const char *name, uint8_t set); static void nat64lsn_stats(const char *name, uint8_t set); static void nat64lsn_reset_stats(const char *name, uint8_t set); static int nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set); static int nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set); static int nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set); static struct _s_x nat64cmds[] = { { "create", TOK_CREATE }, { "config", TOK_CONFIG }, { "destroy", TOK_DESTROY }, { "list", TOK_LIST }, { "show", TOK_LIST }, { "stats", TOK_STATS }, { NULL, 0 } }; static uint64_t nat64lsn_print_states(void *buf) { char s[INET6_ADDRSTRLEN], a[INET_ADDRSTRLEN], f[INET_ADDRSTRLEN]; - char sflags[4], *sf, *proto; + const char *proto; + char sflags[4], *sf; ipfw_obj_header *oh; ipfw_obj_data *od; ipfw_nat64lsn_stg_v1 *stg; ipfw_nat64lsn_state_v1 *ste; uint64_t next_idx; - int i, sz; + uint32_t i; + int sz; oh = (ipfw_obj_header *)buf; od = (ipfw_obj_data *)(oh + 1); stg = (ipfw_nat64lsn_stg_v1 *)(od + 1); sz = od->head.length - sizeof(*od); next_idx = 0; while (sz > 0 && next_idx != 0xFF) { next_idx = stg->next.index; sz -= sizeof(*stg); if (stg->count == 0) { stg++; continue; } /* * NOTE: addresses are in network byte order, * ports are in host byte order. */ inet_ntop(AF_INET, &stg->alias4, a, sizeof(a)); ste = (ipfw_nat64lsn_state_v1 *)(stg + 1); for (i = 0; i < stg->count && sz > 0; i++) { sf = sflags; inet_ntop(AF_INET6, &ste->host6, s, sizeof(s)); inet_ntop(AF_INET, &ste->daddr, f, sizeof(f)); switch (ste->proto) { case IPPROTO_TCP: proto = "TCP"; if (ste->flags & 0x02) *sf++ = 'S'; if (ste->flags & 0x04) *sf++ = 'E'; if (ste->flags & 0x01) *sf++ = 'F'; break; case IPPROTO_UDP: proto = "UDP"; break; case IPPROTO_ICMP: proto = "ICMPv6"; break; } *sf = '\0'; switch (ste->proto) { case IPPROTO_TCP: case IPPROTO_UDP: printf("%s:%d\t%s:%d\t%s\t%s\t%d\t%s:%d\n", s, ste->sport, a, ste->aport, proto, sflags, ste->idle, f, ste->dport); break; case IPPROTO_ICMP: printf("%s\t%s\t%s\t\t%d\t%s\n", s, a, proto, ste->idle, f); break; default: printf("%s\t%s\t%d\t\t%d\t%s\n", s, a, ste->proto, ste->idle, f); } ste++; sz -= sizeof(*ste); } stg = (ipfw_nat64lsn_stg_v1 *)ste; } return (next_idx); } static int nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) { ipfw_obj_header *oh; ipfw_obj_data *od; void *buf; uint64_t next_idx; size_t sz; if (name != NULL && strcmp(cfg->name, name) != 0) return (ESRCH); if (set != 0 && cfg->set != set) return (ESRCH); next_idx = 0; sz = 4096; if ((buf = calloc(1, sz)) == NULL) err(EX_OSERR, NULL); do { oh = (ipfw_obj_header *)buf; oh->opheader.version = 1; /* Force using ov new API */ od = (ipfw_obj_data *)(oh + 1); nat64lsn_fill_ntlv(&oh->ntlv, cfg->name, set); od->head.type = IPFW_TLV_OBJDATA; od->head.length = sizeof(*od) + sizeof(next_idx); *((uint64_t *)(od + 1)) = next_idx; if (do_get3(IP_FW_NAT64LSN_LIST_STATES, &oh->opheader, &sz)) err(EX_OSERR, "Error reading nat64lsn states"); next_idx = nat64lsn_print_states(buf); sz = 4096; memset(buf, 0, sz); } while (next_idx != 0xFF); free(buf); return (0); } static struct _s_x nat64statscmds[] = { { "reset", TOK_RESET }, { NULL, 0 } }; static void ipfw_nat64lsn_stats_handler(const char *name, uint8_t set, int ac, char *av[]) { int tcmd; if (ac == 0) { nat64lsn_stats(name, set); return; } NEED1("nat64lsn stats needs command"); tcmd = get_token(nat64statscmds, *av, "nat64lsn stats command"); switch (tcmd) { case TOK_RESET: nat64lsn_reset_stats(name, set); } } static struct _s_x nat64listcmds[] = { { "states", TOK_STATES }, { "config", TOK_CONFIG }, { NULL, 0 } }; static void ipfw_nat64lsn_list_handler(const char *name, uint8_t set, int ac, char *av[]) { int tcmd; if (ac == 0) { nat64lsn_foreach(nat64lsn_show_cb, name, set, 1); return; } NEED1("nat64lsn list needs command"); tcmd = get_token(nat64listcmds, *av, "nat64lsn list command"); switch (tcmd) { case TOK_STATES: nat64lsn_foreach(nat64lsn_states_cb, name, set, 1); break; case TOK_CONFIG: nat64lsn_foreach(nat64lsn_show_cb, name, set, 1); } } /* * This one handles all nat64lsn-related commands * ipfw [set N] nat64lsn NAME {create | config} ... * ipfw [set N] nat64lsn NAME stats * ipfw [set N] nat64lsn {NAME | all} destroy * ipfw [set N] nat64lsn {NAME | all} {list | show} [config | states] */ #define nat64lsn_check_name table_check_name void ipfw_nat64lsn_handler(int ac, char *av[]) { const char *name; int tcmd; uint8_t set; - if (co.use_set != 0) - set = co.use_set - 1; + if (g_co.use_set != 0) + set = g_co.use_set - 1; else set = 0; ac--; av++; NEED1("nat64lsn needs instance name"); name = *av; if (nat64lsn_check_name(name) != 0) { if (strcmp(name, "all") == 0) name = NULL; else errx(EX_USAGE, "nat64lsn instance name %s is invalid", name); } ac--; av++; NEED1("nat64lsn needs command"); tcmd = get_token(nat64cmds, *av, "nat64lsn command"); if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) errx(EX_USAGE, "nat64lsn instance name required"); switch (tcmd) { case TOK_CREATE: ac--; av++; nat64lsn_create(name, set, ac, av); break; case TOK_CONFIG: ac--; av++; nat64lsn_config(name, set, ac, av); break; case TOK_LIST: ac--; av++; ipfw_nat64lsn_list_handler(name, set, ac, av); break; case TOK_DESTROY: if (name == NULL) nat64lsn_foreach(nat64lsn_destroy_cb, NULL, set, 0); else nat64lsn_destroy(name, set); break; case TOK_STATS: ac--; av++; ipfw_nat64lsn_stats_handler(name, set, ac, av); } } static void nat64lsn_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) { ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = 1; ntlv->set = set; strlcpy(ntlv->name, name, sizeof(ntlv->name)); } static void nat64lsn_apply_mask(int af, void *prefix, uint16_t plen) { struct in6_addr mask6, *p6; struct in_addr mask4, *p4; if (af == AF_INET) { p4 = (struct in_addr *)prefix; mask4.s_addr = htonl(~((1 << (32 - plen)) - 1)); p4->s_addr &= mask4.s_addr; } else if (af == AF_INET6) { p6 = (struct in6_addr *)prefix; n2mask(&mask6, plen); APPLY_MASK(p6, &mask6); } } static void nat64lsn_parse_prefix(const char *arg, int af, void *prefix, uint16_t *plen) { char *p, *l; p = strdup(arg); if (p == NULL) err(EX_OSERR, NULL); if ((l = strchr(p, '/')) != NULL) *l++ = '\0'; if (l == NULL) errx(EX_USAGE, "Prefix length required"); if (inet_pton(af, p, prefix) != 1) errx(EX_USAGE, "Bad prefix: %s", p); *plen = (uint16_t)strtol(l, &l, 10); if (*l != '\0' || *plen == 0 || (af == AF_INET && *plen > 32) || (af == AF_INET6 && *plen > 96)) errx(EX_USAGE, "Bad prefix length: %s", arg); nat64lsn_apply_mask(af, prefix, *plen); free(p); } static uint32_t nat64lsn_parse_int(const char *arg, const char *desc) { char *p; uint32_t val; val = (uint32_t)strtol(arg, &p, 10); if (*p != '\0') errx(EX_USAGE, "Invalid %s value: %s\n", desc, arg); return (val); } static struct _s_x nat64newcmds[] = { { "prefix6", TOK_PREFIX6 }, { "jmaxlen", TOK_JMAXLEN }, { "prefix4", TOK_PREFIX4 }, { "host_del_age", TOK_HOST_DEL_AGE }, { "pg_del_age", TOK_PG_DEL_AGE }, { "tcp_syn_age", TOK_TCP_SYN_AGE }, { "tcp_close_age",TOK_TCP_CLOSE_AGE }, { "tcp_est_age", TOK_TCP_EST_AGE }, { "udp_age", TOK_UDP_AGE }, { "icmp_age", TOK_ICMP_AGE }, { "states_chunks",TOK_STATES_CHUNKS }, { "log", TOK_LOG }, { "-log", TOK_LOGOFF }, { "allow_private", TOK_PRIVATE }, { "-allow_private", TOK_PRIVATEOFF }, /* for compatibility with old configurations */ { "max_ports", TOK_MAX_PORTS }, /* unused */ { NULL, 0 } }; /* * Creates new nat64lsn instance * ipfw nat64lsn create * [ max_ports ] * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ] */ #define NAT64LSN_HAS_PREFIX4 0x01 #define NAT64LSN_HAS_PREFIX6 0x02 static void nat64lsn_create(const char *name, uint8_t set, int ac, char **av) { char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64lsn_cfg)]; ipfw_nat64lsn_cfg *cfg; ipfw_obj_lheader *olh; int tcmd, flags; char *opt; memset(&buf, 0, sizeof(buf)); olh = (ipfw_obj_lheader *)buf; cfg = (ipfw_nat64lsn_cfg *)(olh + 1); /* Some reasonable defaults */ inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6); cfg->plen6 = 96; cfg->set = set; cfg->max_ports = NAT64LSN_MAX_PORTS; cfg->jmaxlen = NAT64LSN_JMAXLEN; cfg->nh_delete_delay = NAT64LSN_HOST_AGE; cfg->pg_delete_delay = NAT64LSN_PG_AGE; cfg->st_syn_ttl = NAT64LSN_TCP_SYN_AGE; cfg->st_estab_ttl = NAT64LSN_TCP_EST_AGE; cfg->st_close_ttl = NAT64LSN_TCP_FIN_AGE; cfg->st_udp_ttl = NAT64LSN_UDP_AGE; cfg->st_icmp_ttl = NAT64LSN_ICMP_AGE; flags = NAT64LSN_HAS_PREFIX6; while (ac > 0) { tcmd = get_token(nat64newcmds, *av, "option"); opt = *av; ac--; av++; switch (tcmd) { case TOK_PREFIX4: NEED1("IPv4 prefix required"); nat64lsn_parse_prefix(*av, AF_INET, &cfg->prefix4, &cfg->plen4); flags |= NAT64LSN_HAS_PREFIX4; ac--; av++; break; case TOK_PREFIX6: NEED1("IPv6 prefix required"); nat64lsn_parse_prefix(*av, AF_INET6, &cfg->prefix6, &cfg->plen6); if (ipfw_check_nat64prefix(&cfg->prefix6, cfg->plen6) != 0 && !IN6_IS_ADDR_UNSPECIFIED(&cfg->prefix6)) errx(EX_USAGE, "Bad prefix6 %s", *av); ac--; av++; break; case TOK_JMAXLEN: NEED1("job queue length required"); cfg->jmaxlen = nat64lsn_parse_int(*av, opt); ac--; av++; break; case TOK_MAX_PORTS: NEED1("Max per-user ports required"); cfg->max_ports = nat64lsn_parse_int(*av, opt); ac--; av++; break; case TOK_HOST_DEL_AGE: NEED1("host delete delay required"); cfg->nh_delete_delay = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_PG_DEL_AGE: NEED1("portgroup delete delay required"); cfg->pg_delete_delay = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_TCP_SYN_AGE: NEED1("tcp syn age required"); cfg->st_syn_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_TCP_CLOSE_AGE: NEED1("tcp close age required"); cfg->st_close_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_TCP_EST_AGE: NEED1("tcp est age required"); cfg->st_estab_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_UDP_AGE: NEED1("udp age required"); cfg->st_udp_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_ICMP_AGE: NEED1("icmp age required"); cfg->st_icmp_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_STATES_CHUNKS: NEED1("number of chunks required"); cfg->states_chunks = (uint8_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_LOG: cfg->flags |= NAT64_LOG; break; case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; case TOK_PRIVATE: cfg->flags |= NAT64_ALLOW_PRIVATE; break; case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; } } /* Check validness */ if ((flags & NAT64LSN_HAS_PREFIX4) != NAT64LSN_HAS_PREFIX4) errx(EX_USAGE, "prefix4 required"); olh->count = 1; olh->objsize = sizeof(*cfg); olh->size = sizeof(buf); strlcpy(cfg->name, name, sizeof(cfg->name)); if (do_set3(IP_FW_NAT64LSN_CREATE, &olh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nat64lsn instance creation failed"); } /* * Configures existing nat64lsn instance * ipfw nat64lsn config * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ] */ static void nat64lsn_config(const char *name, uint8_t set, int ac, char **av) { char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64lsn_cfg)]; ipfw_nat64lsn_cfg *cfg; ipfw_obj_header *oh; size_t sz; char *opt; int tcmd; if (ac == 0) errx(EX_USAGE, "config options required"); memset(&buf, 0, sizeof(buf)); oh = (ipfw_obj_header *)buf; cfg = (ipfw_nat64lsn_cfg *)(oh + 1); sz = sizeof(buf); nat64lsn_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NAT64LSN_CONFIG, &oh->opheader, &sz) != 0) err(EX_OSERR, "failed to get config for instance %s", name); while (ac > 0) { tcmd = get_token(nat64newcmds, *av, "option"); opt = *av; ac--; av++; switch (tcmd) { case TOK_MAX_PORTS: NEED1("Max per-user ports required"); cfg->max_ports = nat64lsn_parse_int(*av, opt); ac--; av++; break; case TOK_JMAXLEN: NEED1("job queue length required"); cfg->jmaxlen = nat64lsn_parse_int(*av, opt); ac--; av++; break; case TOK_HOST_DEL_AGE: NEED1("host delete delay required"); cfg->nh_delete_delay = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_PG_DEL_AGE: NEED1("portgroup delete delay required"); cfg->pg_delete_delay = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_TCP_SYN_AGE: NEED1("tcp syn age required"); cfg->st_syn_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_TCP_CLOSE_AGE: NEED1("tcp close age required"); cfg->st_close_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_TCP_EST_AGE: NEED1("tcp est age required"); cfg->st_estab_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_UDP_AGE: NEED1("udp age required"); cfg->st_udp_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_ICMP_AGE: NEED1("icmp age required"); cfg->st_icmp_ttl = (uint16_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_STATES_CHUNKS: NEED1("number of chunks required"); cfg->states_chunks = (uint8_t)nat64lsn_parse_int( *av, opt); ac--; av++; break; case TOK_LOG: cfg->flags |= NAT64_LOG; break; case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; case TOK_PRIVATE: cfg->flags |= NAT64_ALLOW_PRIVATE; break; case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; default: errx(EX_USAGE, "Can't change %s option", opt); } } if (do_set3(IP_FW_NAT64LSN_CONFIG, &oh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nat64lsn instance configuration failed"); } /* * Reset nat64lsn instance statistics specified by @oh->ntlv. * Request: [ ipfw_obj_header ] */ static void nat64lsn_reset_stats(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nat64lsn_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NAT64LSN_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to reset stats for instance %s", name); } /* * Destroys nat64lsn instance specified by @oh->ntlv. * Request: [ ipfw_obj_header ] */ static void nat64lsn_destroy(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nat64lsn_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NAT64LSN_DESTROY, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to destroy nat instance %s", name); } /* * Get nat64lsn instance statistics. * Request: [ ipfw_obj_header ] * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] */ static int nat64lsn_get_stats(const char *name, uint8_t set, struct ipfw_nat64lsn_stats *stats) { ipfw_obj_header *oh; ipfw_obj_ctlv *oc; size_t sz; sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); oh = calloc(1, sz); nat64lsn_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NAT64LSN_STATS, &oh->opheader, &sz) == 0) { oc = (ipfw_obj_ctlv *)(oh + 1); memcpy(stats, oc + 1, sizeof(*stats)); free(oh); return (0); } free(oh); return (-1); } static void nat64lsn_stats(const char *name, uint8_t set) { struct ipfw_nat64lsn_stats stats; if (nat64lsn_get_stats(name, set, &stats) != 0) err(EX_OSERR, "Error retrieving stats"); - if (co.use_set != 0 || set != 0) + if (g_co.use_set != 0 || set != 0) printf("set %u ", set); printf("nat64lsn %s\n", name); printf("\t%ju packets translated from IPv6 to IPv4\n", (uintmax_t)stats.opcnt64); printf("\t%ju packets translated from IPv4 to IPv6\n", (uintmax_t)stats.opcnt46); printf("\t%ju IPv6 fragments created\n", (uintmax_t)stats.ofrags); printf("\t%ju IPv4 fragments received\n", (uintmax_t)stats.ifrags); printf("\t%ju output packets dropped due to no bufs, etc.\n", (uintmax_t)stats.oerrors); printf("\t%ju output packets discarded due to no IPv4 route\n", (uintmax_t)stats.noroute4); printf("\t%ju output packets discarded due to no IPv6 route\n", (uintmax_t)stats.noroute6); printf("\t%ju packets discarded due to unsupported protocol\n", (uintmax_t)stats.noproto); printf("\t%ju packets discarded due to memory allocation problems\n", (uintmax_t)stats.nomem); printf("\t%ju packets discarded due to some errors\n", (uintmax_t)stats.dropped); printf("\t%ju packets not matched with IPv4 prefix\n", (uintmax_t)stats.nomatch4); printf("\t%ju mbufs queued for post processing\n", (uintmax_t)stats.jreinjected); printf("\t%ju times the job queue was processed\n", (uintmax_t)stats.jcalls); printf("\t%ju job requests queued\n", (uintmax_t)stats.jrequests); printf("\t%ju job requests queue limit reached\n", (uintmax_t)stats.jmaxlen); printf("\t%ju job requests failed due to memory allocation problems\n", (uintmax_t)stats.jnomem); printf("\t%ju hosts allocated\n", (uintmax_t)stats.hostcount); printf("\t%ju hosts requested\n", (uintmax_t)stats.jhostsreq); printf("\t%ju host requests failed\n", (uintmax_t)stats.jhostfails); printf("\t%ju portgroups requested\n", (uintmax_t)stats.jportreq); printf("\t%ju portgroups allocated\n", (uintmax_t)stats.spgcreated); printf("\t%ju portgroups deleted\n", (uintmax_t)stats.spgdeleted); printf("\t%ju portgroup requests failed\n", (uintmax_t)stats.jportfails); printf("\t%ju portgroups allocated for TCP\n", (uintmax_t)stats.tcpchunks); printf("\t%ju portgroups allocated for UDP\n", (uintmax_t)stats.udpchunks); printf("\t%ju portgroups allocated for ICMP\n", (uintmax_t)stats.icmpchunks); printf("\t%ju states created\n", (uintmax_t)stats.screated); printf("\t%ju states deleted\n", (uintmax_t)stats.sdeleted); } static int nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) { char abuf[INET6_ADDRSTRLEN]; if (name != NULL && strcmp(cfg->name, name) != 0) return (ESRCH); - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); - if (co.use_set != 0 || cfg->set != 0) + if (g_co.use_set != 0 || cfg->set != 0) printf("set %u ", cfg->set); inet_ntop(AF_INET, &cfg->prefix4, abuf, sizeof(abuf)); printf("nat64lsn %s prefix4 %s/%u", cfg->name, abuf, cfg->plen4); inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf)); printf(" prefix6 %s/%u", abuf, cfg->plen6); - if (co.verbose || cfg->states_chunks > 1) + if (g_co.verbose || cfg->states_chunks > 1) printf(" states_chunks %u", cfg->states_chunks); - if (co.verbose || cfg->nh_delete_delay != NAT64LSN_HOST_AGE) + if (g_co.verbose || cfg->nh_delete_delay != NAT64LSN_HOST_AGE) printf(" host_del_age %u", cfg->nh_delete_delay); - if (co.verbose || cfg->pg_delete_delay != NAT64LSN_PG_AGE) + if (g_co.verbose || cfg->pg_delete_delay != NAT64LSN_PG_AGE) printf(" pg_del_age %u", cfg->pg_delete_delay); - if (co.verbose || cfg->st_syn_ttl != NAT64LSN_TCP_SYN_AGE) + if (g_co.verbose || cfg->st_syn_ttl != NAT64LSN_TCP_SYN_AGE) printf(" tcp_syn_age %u", cfg->st_syn_ttl); - if (co.verbose || cfg->st_close_ttl != NAT64LSN_TCP_FIN_AGE) + if (g_co.verbose || cfg->st_close_ttl != NAT64LSN_TCP_FIN_AGE) printf(" tcp_close_age %u", cfg->st_close_ttl); - if (co.verbose || cfg->st_estab_ttl != NAT64LSN_TCP_EST_AGE) + if (g_co.verbose || cfg->st_estab_ttl != NAT64LSN_TCP_EST_AGE) printf(" tcp_est_age %u", cfg->st_estab_ttl); - if (co.verbose || cfg->st_udp_ttl != NAT64LSN_UDP_AGE) + if (g_co.verbose || cfg->st_udp_ttl != NAT64LSN_UDP_AGE) printf(" udp_age %u", cfg->st_udp_ttl); - if (co.verbose || cfg->st_icmp_ttl != NAT64LSN_ICMP_AGE) + if (g_co.verbose || cfg->st_icmp_ttl != NAT64LSN_ICMP_AGE) printf(" icmp_age %u", cfg->st_icmp_ttl); - if (co.verbose || cfg->jmaxlen != NAT64LSN_JMAXLEN) + if (g_co.verbose || cfg->jmaxlen != NAT64LSN_JMAXLEN) printf(" jmaxlen %u", cfg->jmaxlen); if (cfg->flags & NAT64_LOG) printf(" log"); if (cfg->flags & NAT64_ALLOW_PRIVATE) printf(" allow_private"); printf("\n"); return (0); } static int -nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) +nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name __unused, + uint8_t set) { - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); nat64lsn_destroy(cfg->name, cfg->set); return (0); } /* * Compare nat64lsn instances names. * Honor number comparison. */ static int nat64name_cmp(const void *a, const void *b) { - ipfw_nat64lsn_cfg *ca, *cb; + const ipfw_nat64lsn_cfg *ca, *cb; - ca = (ipfw_nat64lsn_cfg *)a; - cb = (ipfw_nat64lsn_cfg *)b; + ca = (const ipfw_nat64lsn_cfg *)a; + cb = (const ipfw_nat64lsn_cfg *)b; if (ca->set > cb->set) return (1); else if (ca->set < cb->set) return (-1); return (stringnum_cmp(ca->name, cb->name)); } /* * Retrieves nat64lsn instance list from kernel, * optionally sorts it and calls requested function for each instance. * * Request: [ ipfw_obj_lheader ] * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ] */ static int nat64lsn_foreach(nat64lsn_cb_t *f, const char *name, uint8_t set, int sort) { ipfw_obj_lheader *olh; ipfw_nat64lsn_cfg *cfg; size_t sz; - int i, error; + uint32_t i; + int error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(ipfw_nat64lsn_cfg); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_NAT64LSN_LIST, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno != ENOMEM) return (errno); continue; } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, nat64name_cmp); cfg = (ipfw_nat64lsn_cfg *)(olh + 1); for (i = 0; i < olh->count; i++) { error = f(cfg, name, set); /* Ignore errors for now */ cfg = (ipfw_nat64lsn_cfg *)((caddr_t)cfg + olh->objsize); } free(olh); break; } return (0); } Index: head/sbin/ipfw/nat64stl.c =================================================================== --- head/sbin/ipfw/nat64stl.c (revision 363163) +++ head/sbin/ipfw/nat64stl.c (revision 363164) @@ -1,551 +1,553 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2015-2019 Yandex LLC * Copyright (c) 2015-2019 Andrey V. Elsukov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef int (nat64stl_cb_t)(ipfw_nat64stl_cfg *i, const char *name, uint8_t set); static int nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort); static void nat64stl_create(const char *name, uint8_t set, int ac, char **av); static void nat64stl_config(const char *name, uint8_t set, int ac, char **av); static void nat64stl_destroy(const char *name, uint8_t set); static void nat64stl_stats(const char *name, uint8_t set); static void nat64stl_reset_stats(const char *name, uint8_t set); static int nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set); static int nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set); static struct _s_x nat64cmds[] = { { "create", TOK_CREATE }, { "config", TOK_CONFIG }, { "destroy", TOK_DESTROY }, { "list", TOK_LIST }, { "show", TOK_LIST }, { "stats", TOK_STATS }, { NULL, 0 } }; #define IPV6_ADDR_INT32_WKPFX htonl(0x64ff9b) #define IN6_IS_ADDR_WKPFX(a) \ ((a)->__u6_addr.__u6_addr32[0] == IPV6_ADDR_INT32_WKPFX && \ (a)->__u6_addr.__u6_addr32[1] == 0 && \ (a)->__u6_addr.__u6_addr32[2] == 0) int ipfw_check_nat64prefix(const struct in6_addr *prefix, int length) { switch (length) { case 32: case 40: case 48: case 56: case 64: /* Well-known prefix has 96 prefix length */ if (IN6_IS_ADDR_WKPFX(prefix)) return (EINVAL); /* FALLTHROUGH */ case 96: /* Bits 64 to 71 must be set to zero */ if (prefix->__u6_addr.__u6_addr8[8] != 0) return (EINVAL); /* XXX: looks incorrect */ if (IN6_IS_ADDR_MULTICAST(prefix) || IN6_IS_ADDR_UNSPECIFIED(prefix) || IN6_IS_ADDR_LOOPBACK(prefix)) return (EINVAL); return (0); } return (EINVAL); } static struct _s_x nat64statscmds[] = { { "reset", TOK_RESET }, { NULL, 0 } }; /* * This one handles all nat64stl-related commands * ipfw [set N] nat64stl NAME {create | config} ... * ipfw [set N] nat64stl NAME stats [reset] * ipfw [set N] nat64stl {NAME | all} destroy * ipfw [set N] nat64stl {NAME | all} {list | show} */ #define nat64stl_check_name table_check_name void ipfw_nat64stl_handler(int ac, char *av[]) { const char *name; int tcmd; uint8_t set; - if (co.use_set != 0) - set = co.use_set - 1; + if (g_co.use_set != 0) + set = g_co.use_set - 1; else set = 0; ac--; av++; NEED1("nat64stl needs instance name"); name = *av; if (nat64stl_check_name(name) != 0) { if (strcmp(name, "all") == 0) name = NULL; else errx(EX_USAGE, "nat64stl instance name %s is invalid", name); } ac--; av++; NEED1("nat64stl needs command"); tcmd = get_token(nat64cmds, *av, "nat64stl command"); if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) errx(EX_USAGE, "nat64stl instance name required"); switch (tcmd) { case TOK_CREATE: ac--; av++; nat64stl_create(name, set, ac, av); break; case TOK_CONFIG: ac--; av++; nat64stl_config(name, set, ac, av); break; case TOK_LIST: nat64stl_foreach(nat64stl_show_cb, name, set, 1); break; case TOK_DESTROY: if (name == NULL) nat64stl_foreach(nat64stl_destroy_cb, NULL, set, 0); else nat64stl_destroy(name, set); break; case TOK_STATS: ac--; av++; if (ac == 0) { nat64stl_stats(name, set); break; } tcmd = get_token(nat64statscmds, *av, "stats command"); if (tcmd == TOK_RESET) nat64stl_reset_stats(name, set); } } static void nat64stl_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) { ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = 1; ntlv->set = set; strlcpy(ntlv->name, name, sizeof(ntlv->name)); } static struct _s_x nat64newcmds[] = { { "table4", TOK_TABLE4 }, { "table6", TOK_TABLE6 }, { "prefix6", TOK_PREFIX6 }, { "log", TOK_LOG }, { "-log", TOK_LOGOFF }, { "allow_private", TOK_PRIVATE }, { "-allow_private", TOK_PRIVATEOFF }, { NULL, 0 } }; /* * Creates new nat64stl instance * ipfw nat64stl create table4 table6 [ prefix6 ] * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ] */ #define NAT64STL_HAS_TABLE4 0x01 #define NAT64STL_HAS_TABLE6 0x02 #define NAT64STL_HAS_PREFIX6 0x04 static void nat64stl_create(const char *name, uint8_t set, int ac, char *av[]) { char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64stl_cfg)]; ipfw_nat64stl_cfg *cfg; ipfw_obj_lheader *olh; int tcmd, flags; char *p; memset(buf, 0, sizeof(buf)); olh = (ipfw_obj_lheader *)buf; cfg = (ipfw_nat64stl_cfg *)(olh + 1); /* Some reasonable defaults */ inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6); cfg->plen6 = 96; cfg->set = set; flags = NAT64STL_HAS_PREFIX6; while (ac > 0) { tcmd = get_token(nat64newcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_TABLE4: NEED1("table name required"); table_fill_ntlv(&cfg->ntlv4, *av, set, 4); flags |= NAT64STL_HAS_TABLE4; ac--; av++; break; case TOK_TABLE6: NEED1("table name required"); table_fill_ntlv(&cfg->ntlv6, *av, set, 6); flags |= NAT64STL_HAS_TABLE6; ac--; av++; break; case TOK_PREFIX6: NEED1("IPv6 prefix6 required"); if ((p = strchr(*av, '/')) != NULL) *p++ = '\0'; if (inet_pton(AF_INET6, *av, &cfg->prefix6) != 1) errx(EX_USAGE, "Bad prefix: %s", *av); cfg->plen6 = strtol(p, NULL, 10); if (ipfw_check_nat64prefix(&cfg->prefix6, cfg->plen6) != 0) errx(EX_USAGE, "Bad prefix length: %s", p); flags |= NAT64STL_HAS_PREFIX6; ac--; av++; break; case TOK_LOG: cfg->flags |= NAT64_LOG; break; case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; case TOK_PRIVATE: cfg->flags |= NAT64_ALLOW_PRIVATE; break; case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; } } /* Check validness */ if ((flags & NAT64STL_HAS_TABLE4) != NAT64STL_HAS_TABLE4) errx(EX_USAGE, "table4 required"); if ((flags & NAT64STL_HAS_TABLE6) != NAT64STL_HAS_TABLE6) errx(EX_USAGE, "table6 required"); if ((flags & NAT64STL_HAS_PREFIX6) != NAT64STL_HAS_PREFIX6) errx(EX_USAGE, "prefix6 required"); olh->count = 1; olh->objsize = sizeof(*cfg); olh->size = sizeof(buf); strlcpy(cfg->name, name, sizeof(cfg->name)); if (do_set3(IP_FW_NAT64STL_CREATE, &olh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nat64stl instance creation failed"); } /* * Configures existing nat64stl instance * ipfw nat64stl config * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ] */ static void nat64stl_config(const char *name, uint8_t set, int ac, char **av) { char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64stl_cfg)]; ipfw_nat64stl_cfg *cfg; ipfw_obj_header *oh; char *opt; size_t sz; int tcmd; if (ac == 0) errx(EX_USAGE, "config options required"); memset(&buf, 0, sizeof(buf)); oh = (ipfw_obj_header *)buf; cfg = (ipfw_nat64stl_cfg *)(oh + 1); sz = sizeof(buf); nat64stl_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NAT64STL_CONFIG, &oh->opheader, &sz) != 0) err(EX_OSERR, "failed to get config for instance %s", name); while (ac > 0) { tcmd = get_token(nat64newcmds, *av, "option"); opt = *av; ac--; av++; switch (tcmd) { #if 0 case TOK_TABLE4: NEED1("table name required"); table_fill_ntlv(&cfg->ntlv4, *av, set, 4); ac--; av++; break; case TOK_TABLE6: NEED1("table name required"); table_fill_ntlv(&cfg->ntlv6, *av, set, 6); ac--; av++; break; #endif case TOK_LOG: cfg->flags |= NAT64_LOG; break; case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; case TOK_PRIVATE: cfg->flags |= NAT64_ALLOW_PRIVATE; break; case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; default: errx(EX_USAGE, "Can't change %s option", opt); } } if (do_set3(IP_FW_NAT64STL_CONFIG, &oh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nat64stl instance configuration failed"); } /* * Destroys nat64stl instance. * Request: [ ipfw_obj_header ] */ static void nat64stl_destroy(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nat64stl_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NAT64STL_DESTROY, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to destroy nat instance %s", name); } /* * Get nat64stl instance statistics. * Request: [ ipfw_obj_header ] * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] */ static int nat64stl_get_stats(const char *name, uint8_t set, struct ipfw_nat64stl_stats *stats) { ipfw_obj_header *oh; ipfw_obj_ctlv *oc; size_t sz; sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); oh = calloc(1, sz); nat64stl_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NAT64STL_STATS, &oh->opheader, &sz) == 0) { oc = (ipfw_obj_ctlv *)(oh + 1); memcpy(stats, oc + 1, sizeof(*stats)); free(oh); return (0); } free(oh); return (-1); } static void nat64stl_stats(const char *name, uint8_t set) { struct ipfw_nat64stl_stats stats; if (nat64stl_get_stats(name, set, &stats) != 0) err(EX_OSERR, "Error retrieving stats"); - if (co.use_set != 0 || set != 0) + if (g_co.use_set != 0 || set != 0) printf("set %u ", set); printf("nat64stl %s\n", name); printf("\t%ju packets translated from IPv6 to IPv4\n", (uintmax_t)stats.opcnt64); printf("\t%ju packets translated from IPv4 to IPv6\n", (uintmax_t)stats.opcnt46); printf("\t%ju IPv6 fragments created\n", (uintmax_t)stats.ofrags); printf("\t%ju IPv4 fragments received\n", (uintmax_t)stats.ifrags); printf("\t%ju output packets dropped due to no bufs, etc.\n", (uintmax_t)stats.oerrors); printf("\t%ju output packets discarded due to no IPv4 route\n", (uintmax_t)stats.noroute4); printf("\t%ju output packets discarded due to no IPv6 route\n", (uintmax_t)stats.noroute6); printf("\t%ju packets discarded due to unsupported protocol\n", (uintmax_t)stats.noproto); printf("\t%ju packets discarded due to memory allocation problems\n", (uintmax_t)stats.nomem); printf("\t%ju packets discarded due to some errors\n", (uintmax_t)stats.dropped); } /* * Reset nat64stl instance statistics specified by @oh->ntlv. * Request: [ ipfw_obj_header ] */ static void nat64stl_reset_stats(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nat64stl_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NAT64STL_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to reset stats for instance %s", name); } static int nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) { char abuf[INET6_ADDRSTRLEN]; if (name != NULL && strcmp(cfg->name, name) != 0) return (ESRCH); - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); - if (co.use_set != 0 || cfg->set != 0) + if (g_co.use_set != 0 || cfg->set != 0) printf("set %u ", cfg->set); printf("nat64stl %s table4 %s table6 %s", cfg->name, cfg->ntlv4.name, cfg->ntlv6.name); inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf)); printf(" prefix6 %s/%u", abuf, cfg->plen6); if (cfg->flags & NAT64_LOG) printf(" log"); if (cfg->flags & NAT64_ALLOW_PRIVATE) printf(" allow_private"); printf("\n"); return (0); } static int -nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) +nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name __unused, + uint8_t set) { - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); nat64stl_destroy(cfg->name, cfg->set); return (0); } /* * Compare nat64stl instances names. * Honor number comparison. */ static int nat64name_cmp(const void *a, const void *b) { - ipfw_nat64stl_cfg *ca, *cb; + const ipfw_nat64stl_cfg *ca, *cb; - ca = (ipfw_nat64stl_cfg *)a; - cb = (ipfw_nat64stl_cfg *)b; + ca = (const ipfw_nat64stl_cfg *)a; + cb = (const ipfw_nat64stl_cfg *)b; if (ca->set > cb->set) return (1); else if (ca->set < cb->set) return (-1); return (stringnum_cmp(ca->name, cb->name)); } /* * Retrieves nat64stl instance list from kernel, * optionally sorts it and calls requested function for each instance. * * Request: [ ipfw_obj_lheader ] * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ] */ static int nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort) { ipfw_obj_lheader *olh; ipfw_nat64stl_cfg *cfg; size_t sz; - int i, error; + uint32_t i; + int error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(*cfg); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_NAT64STL_LIST, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno != ENOMEM) return (errno); continue; } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, nat64name_cmp); cfg = (ipfw_nat64stl_cfg *)(olh + 1); for (i = 0; i < olh->count; i++) { error = f(cfg, name, set); /* Ignore errors for now */ cfg = (ipfw_nat64stl_cfg *)((caddr_t)cfg + olh->objsize); } free(olh); break; } return (0); } Index: head/sbin/ipfw/nptv6.c =================================================================== --- head/sbin/ipfw/nptv6.c (revision 363163) +++ head/sbin/ipfw/nptv6.c (revision 363164) @@ -1,452 +1,453 @@ /*- * Copyright (c) 2016 Yandex LLC * Copyright (c) 2016 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include typedef int (nptv6_cb_t)(ipfw_nptv6_cfg *i, const char *name, uint8_t set); static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort); static void nptv6_create(const char *name, uint8_t set, int ac, char **av); static void nptv6_destroy(const char *name, uint8_t set); static void nptv6_stats(const char *name, uint8_t set); static void nptv6_reset_stats(const char *name, uint8_t set); static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set); static int nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set); static struct _s_x nptv6cmds[] = { { "create", TOK_CREATE }, { "destroy", TOK_DESTROY }, { "list", TOK_LIST }, { "show", TOK_LIST }, { "stats", TOK_STATS }, { NULL, 0 } }; static struct _s_x nptv6statscmds[] = { { "reset", TOK_RESET }, { NULL, 0 } }; /* * This one handles all NPTv6-related commands * ipfw [set N] nptv6 NAME {create | config} ... * ipfw [set N] nptv6 NAME stats [reset] * ipfw [set N] nptv6 {NAME | all} destroy * ipfw [set N] nptv6 {NAME | all} {list | show} */ #define nptv6_check_name table_check_name void ipfw_nptv6_handler(int ac, char *av[]) { const char *name; int tcmd; uint8_t set; - if (co.use_set != 0) - set = co.use_set - 1; + if (g_co.use_set != 0) + set = g_co.use_set - 1; else set = 0; ac--; av++; NEED1("nptv6 needs instance name"); name = *av; if (nptv6_check_name(name) != 0) { if (strcmp(name, "all") == 0) { name = NULL; } else errx(EX_USAGE, "nptv6 instance name %s is invalid", name); } ac--; av++; NEED1("nptv6 needs command"); tcmd = get_token(nptv6cmds, *av, "nptv6 command"); if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) errx(EX_USAGE, "nptv6 instance name required"); switch (tcmd) { case TOK_CREATE: ac--; av++; nptv6_create(name, set, ac, av); break; case TOK_LIST: nptv6_foreach(nptv6_show_cb, name, set, 1); break; case TOK_DESTROY: if (name == NULL) nptv6_foreach(nptv6_destroy_cb, NULL, set, 0); else nptv6_destroy(name, set); break; case TOK_STATS: ac--; av++; if (ac == 0) { nptv6_stats(name, set); break; } tcmd = get_token(nptv6statscmds, *av, "stats command"); if (tcmd == TOK_RESET) nptv6_reset_stats(name, set); } } static void nptv6_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) { ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = 1; ntlv->set = set; strlcpy(ntlv->name, name, sizeof(ntlv->name)); } static struct _s_x nptv6newcmds[] = { { "int_prefix", TOK_INTPREFIX }, { "ext_prefix", TOK_EXTPREFIX }, { "prefixlen", TOK_PREFIXLEN }, { "ext_if", TOK_EXTIF }, { NULL, 0 } }; static void nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len) { char *p, *l; p = strdup(arg); if (p == NULL) err(EX_OSERR, NULL); if ((l = strchr(p, '/')) != NULL) *l++ = '\0'; if (inet_pton(AF_INET6, p, prefix) != 1) errx(EX_USAGE, "Bad prefix: %s", p); if (l != NULL) { *len = (int)strtol(l, &l, 10); if (*l != '\0' || *len <= 0 || *len > 64) errx(EX_USAGE, "Bad prefix length: %s", arg); } else *len = 0; free(p); } /* * Creates new nptv6 instance * ipfw nptv6 create int_prefix ext_prefix * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ] */ #define NPTV6_HAS_INTPREFIX 0x01 #define NPTV6_HAS_EXTPREFIX 0x02 #define NPTV6_HAS_PREFIXLEN 0x04 static void nptv6_create(const char *name, uint8_t set, int ac, char *av[]) { char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nptv6_cfg)]; struct in6_addr mask; ipfw_nptv6_cfg *cfg; ipfw_obj_lheader *olh; int tcmd, flags, plen; - char *p = "\0"; + char *p; plen = 0; memset(buf, 0, sizeof(buf)); olh = (ipfw_obj_lheader *)buf; cfg = (ipfw_nptv6_cfg *)(olh + 1); cfg->set = set; flags = 0; while (ac > 0) { tcmd = get_token(nptv6newcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_INTPREFIX: NEED1("IPv6 prefix required"); nptv6_parse_prefix(*av, &cfg->internal, &plen); flags |= NPTV6_HAS_INTPREFIX; if (plen > 0) goto check_prefix; ac--; av++; break; case TOK_EXTPREFIX: if (flags & NPTV6_HAS_EXTPREFIX) errx(EX_USAGE, "Only one ext_prefix or ext_if allowed"); NEED1("IPv6 prefix required"); nptv6_parse_prefix(*av, &cfg->external, &plen); flags |= NPTV6_HAS_EXTPREFIX; if (plen > 0) goto check_prefix; ac--; av++; break; case TOK_EXTIF: if (flags & NPTV6_HAS_EXTPREFIX) errx(EX_USAGE, "Only one ext_prefix or ext_if allowed"); NEED1("Interface name required"); if (strlen(*av) >= sizeof(cfg->if_name)) errx(EX_USAGE, "Invalid interface name"); flags |= NPTV6_HAS_EXTPREFIX; cfg->flags |= NPTV6_DYNAMIC_PREFIX; strncpy(cfg->if_name, *av, sizeof(cfg->if_name)); ac--; av++; break; case TOK_PREFIXLEN: NEED1("IPv6 prefix length required"); plen = strtol(*av, &p, 10); check_prefix: if (*p != '\0' || plen < 8 || plen > 64) errx(EX_USAGE, "wrong prefix length: %s", *av); /* RFC 6296 Sec. 3.1 */ if (cfg->plen > 0 && cfg->plen != plen) { warnx("Prefix length mismatch (%d vs %d). " "It was extended up to %d", cfg->plen, plen, MAX(plen, cfg->plen)); plen = MAX(plen, cfg->plen); } cfg->plen = plen; flags |= NPTV6_HAS_PREFIXLEN; ac--; av++; break; } } /* Check validness */ if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX) errx(EX_USAGE, "int_prefix required"); if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX) errx(EX_USAGE, "ext_prefix or ext_if required"); if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN) errx(EX_USAGE, "prefixlen required"); n2mask(&mask, cfg->plen); APPLY_MASK(&cfg->internal, &mask); if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0) APPLY_MASK(&cfg->external, &mask); olh->count = 1; olh->objsize = sizeof(*cfg); olh->size = sizeof(buf); strlcpy(cfg->name, name, sizeof(cfg->name)); if (do_set3(IP_FW_NPTV6_CREATE, &olh->opheader, sizeof(buf)) != 0) err(EX_OSERR, "nptv6 instance creation failed"); } /* * Destroys NPTv6 instance. * Request: [ ipfw_obj_header ] */ static void nptv6_destroy(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nptv6_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NPTV6_DESTROY, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to destroy nat instance %s", name); } /* * Get NPTv6 instance statistics. * Request: [ ipfw_obj_header ] * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] */ static int nptv6_get_stats(const char *name, uint8_t set, struct ipfw_nptv6_stats *stats) { ipfw_obj_header *oh; ipfw_obj_ctlv *oc; size_t sz; sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); oh = calloc(1, sz); nptv6_fill_ntlv(&oh->ntlv, name, set); if (do_get3(IP_FW_NPTV6_STATS, &oh->opheader, &sz) == 0) { oc = (ipfw_obj_ctlv *)(oh + 1); memcpy(stats, oc + 1, sizeof(*stats)); free(oh); return (0); } free(oh); return (-1); } static void nptv6_stats(const char *name, uint8_t set) { struct ipfw_nptv6_stats stats; if (nptv6_get_stats(name, set, &stats) != 0) err(EX_OSERR, "Error retrieving stats"); - if (co.use_set != 0 || set != 0) + if (g_co.use_set != 0 || set != 0) printf("set %u ", set); printf("nptv6 %s\n", name); printf("\t%ju packets translated (internal to external)\n", (uintmax_t)stats.in2ex); printf("\t%ju packets translated (external to internal)\n", (uintmax_t)stats.ex2in); printf("\t%ju packets dropped due to some error\n", (uintmax_t)stats.dropped); } /* * Reset NPTv6 instance statistics specified by @oh->ntlv. * Request: [ ipfw_obj_header ] */ static void nptv6_reset_stats(const char *name, uint8_t set) { ipfw_obj_header oh; memset(&oh, 0, sizeof(oh)); nptv6_fill_ntlv(&oh.ntlv, name, set); if (do_set3(IP_FW_NPTV6_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) err(EX_OSERR, "failed to reset stats for instance %s", name); } static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set) { char abuf[INET6_ADDRSTRLEN]; if (name != NULL && strcmp(cfg->name, name) != 0) return (ESRCH); - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); - if (co.use_set != 0 || cfg->set != 0) + if (g_co.use_set != 0 || cfg->set != 0) printf("set %u ", cfg->set); inet_ntop(AF_INET6, &cfg->internal, abuf, sizeof(abuf)); printf("nptv6 %s int_prefix %s ", cfg->name, abuf); if (cfg->flags & NPTV6_DYNAMIC_PREFIX) printf("ext_if %s ", cfg->if_name); else { inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf)); printf("ext_prefix %s ", abuf); } printf("prefixlen %u\n", cfg->plen); return (0); } static int -nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set) +nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name __unused, uint8_t set) { - if (co.use_set != 0 && cfg->set != set) + if (g_co.use_set != 0 && cfg->set != set) return (ESRCH); nptv6_destroy(cfg->name, cfg->set); return (0); } /* * Compare NPTv6 instances names. * Honor number comparison. */ static int nptv6name_cmp(const void *a, const void *b) { - ipfw_nptv6_cfg *ca, *cb; + const ipfw_nptv6_cfg *ca, *cb; - ca = (ipfw_nptv6_cfg *)a; - cb = (ipfw_nptv6_cfg *)b; + ca = (const ipfw_nptv6_cfg *)a; + cb = (const ipfw_nptv6_cfg *)b; if (ca->set > cb->set) return (1); else if (ca->set < cb->set) return (-1); return (stringnum_cmp(ca->name, cb->name)); } /* * Retrieves NPTv6 instance list from kernel, * Request: [ ipfw_obj_lheader ] * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ] */ static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort) { ipfw_obj_lheader *olh; ipfw_nptv6_cfg *cfg; size_t sz; - int i, error; + uint32_t i; + int error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(*cfg); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_NPTV6_LIST, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno != ENOMEM) return (errno); continue; } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, nptv6name_cmp); cfg = (ipfw_nptv6_cfg *)(olh + 1); for (i = 0; i < olh->count; i++) { error = f(cfg, name, set); cfg = (ipfw_nptv6_cfg *)((caddr_t)cfg + olh->objsize); } free(olh); break; } return (0); } Index: head/sbin/ipfw/tables.c =================================================================== --- head/sbin/ipfw/tables.c (revision 363163) +++ head/sbin/ipfw/tables.c (revision 363164) @@ -1,2065 +1,2068 @@ /* * Copyright (c) 2014 Yandex LLC * Copyright (c) 2014 Alexander V. Chernikov * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * in-kernel ipfw tables support. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipfw2.h" static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int quiet, int update, int atomic); static int table_flush(ipfw_obj_header *oh); static int table_destroy(ipfw_obj_header *oh); static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_do_swap(ipfw_obj_header *oh, char *second); static void table_create(ipfw_obj_header *oh, int ac, char *av[]); static void table_modify(ipfw_obj_header *oh, int ac, char *av[]); static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]); static void table_lock(ipfw_obj_header *oh, int lock); static int table_swap(ipfw_obj_header *oh, char *second); static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_show_info(ipfw_xtable_info *i, void *arg); static int table_destroy_one(ipfw_xtable_info *i, void *arg); static int table_flush_one(ipfw_xtable_info *i, void *arg); static int table_show_one(ipfw_xtable_info *i, void *arg); static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh); static void table_show_list(ipfw_obj_header *oh, int need_header); static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent); static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi); static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, uint8_t type, uint32_t vmask); static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v, uint32_t vmask, int print_ip); typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg); static int tables_foreach(table_cb_t *f, void *arg, int sort); #ifndef s6_addr32 #define s6_addr32 __u6_addr.__u6_addr32 #endif static struct _s_x tabletypes[] = { { "addr", IPFW_TABLE_ADDR }, { "iface", IPFW_TABLE_INTERFACE }, { "number", IPFW_TABLE_NUMBER }, { "flow", IPFW_TABLE_FLOW }, { NULL, 0 } }; static struct _s_x tablevaltypes[] = { { "skipto", IPFW_VTYPE_SKIPTO }, { "pipe", IPFW_VTYPE_PIPE }, { "fib", IPFW_VTYPE_FIB }, { "nat", IPFW_VTYPE_NAT }, { "dscp", IPFW_VTYPE_DSCP }, { "tag", IPFW_VTYPE_TAG }, { "divert", IPFW_VTYPE_DIVERT }, { "netgraph", IPFW_VTYPE_NETGRAPH }, { "limit", IPFW_VTYPE_LIMIT }, { "ipv4", IPFW_VTYPE_NH4 }, { "ipv6", IPFW_VTYPE_NH6 }, { NULL, 0 } }; static struct _s_x tablecmds[] = { { "add", TOK_ADD }, { "delete", TOK_DEL }, { "create", TOK_CREATE }, { "destroy", TOK_DESTROY }, { "flush", TOK_FLUSH }, { "modify", TOK_MODIFY }, { "swap", TOK_SWAP }, { "info", TOK_INFO }, { "detail", TOK_DETAIL }, { "list", TOK_LIST }, { "lookup", TOK_LOOKUP }, { "atomic", TOK_ATOMIC }, { "lock", TOK_LOCK }, { "unlock", TOK_UNLOCK }, { NULL, 0 } }; static int lookup_host (char *host, struct in_addr *ipaddr) { struct hostent *he; if (!inet_aton(host, ipaddr)) { if ((he = gethostbyname(host)) == NULL) return(-1); *ipaddr = *(struct in_addr *)he->h_addr_list[0]; } return(0); } /* * This one handles all table-related commands * ipfw table NAME create ... * ipfw table NAME modify ... * ipfw table {NAME | all} destroy * ipfw table NAME swap NAME * ipfw table NAME lock * ipfw table NAME unlock * ipfw table NAME add addr[/masklen] [value] * ipfw table NAME add [addr[/masklen] value] [addr[/masklen] value] .. * ipfw table NAME delete addr[/masklen] [addr[/masklen]] .. * ipfw table NAME lookup addr * ipfw table {NAME | all} flush * ipfw table {NAME | all} list * ipfw table {NAME | all} info * ipfw table {NAME | all} detail */ void ipfw_table_handler(int ac, char *av[]) { int do_add, is_all; int atomic, error, tcmd; ipfw_xtable_info i; ipfw_obj_header oh; char *tablename; uint8_t set; void *arg; memset(&oh, 0, sizeof(oh)); is_all = 0; - if (co.use_set != 0) - set = co.use_set - 1; + if (g_co.use_set != 0) + set = g_co.use_set - 1; else set = 0; ac--; av++; NEED1("table needs name"); tablename = *av; if (table_check_name(tablename) == 0) { table_fill_ntlv(&oh.ntlv, *av, set, 1); oh.idx = 1; } else { if (strcmp(tablename, "all") == 0) is_all = 1; else errx(EX_USAGE, "table name %s is invalid", tablename); } ac--; av++; NEED1("table needs command"); tcmd = get_token(tablecmds, *av, "table command"); /* Check if atomic operation was requested */ atomic = 0; if (tcmd == TOK_ATOMIC) { ac--; av++; NEED1("atomic needs command"); tcmd = get_token(tablecmds, *av, "table command"); switch (tcmd) { case TOK_ADD: break; default: errx(EX_USAGE, "atomic is not compatible with %s", *av); } atomic = 1; } switch (tcmd) { case TOK_LIST: case TOK_INFO: case TOK_DETAIL: case TOK_FLUSH: case TOK_DESTROY: break; default: if (is_all != 0) errx(EX_USAGE, "table name required"); } switch (tcmd) { case TOK_ADD: case TOK_DEL: do_add = **av == 'a'; ac--; av++; - table_modify_record(&oh, ac, av, do_add, co.do_quiet, - co.do_quiet, atomic); + table_modify_record(&oh, ac, av, do_add, g_co.do_quiet, + g_co.do_quiet, atomic); break; case TOK_CREATE: ac--; av++; table_create(&oh, ac, av); break; case TOK_MODIFY: ac--; av++; table_modify(&oh, ac, av); break; case TOK_DESTROY: if (is_all == 0) { if (table_destroy(&oh) == 0) break; if (errno != ESRCH) err(EX_OSERR, "failed to destroy table %s", tablename); /* ESRCH isn't fatal, warn if not quiet mode */ - if (co.do_quiet == 0) + if (g_co.do_quiet == 0) warn("failed to destroy table %s", tablename); } else { error = tables_foreach(table_destroy_one, &oh, 1); if (error != 0) err(EX_OSERR, "failed to destroy tables list"); } break; case TOK_FLUSH: if (is_all == 0) { if ((error = table_flush(&oh)) == 0) break; if (errno != ESRCH) err(EX_OSERR, "failed to flush table %s info", tablename); /* ESRCH isn't fatal, warn if not quiet mode */ - if (co.do_quiet == 0) + if (g_co.do_quiet == 0) warn("failed to flush table %s info", tablename); } else { error = tables_foreach(table_flush_one, &oh, 1); if (error != 0) err(EX_OSERR, "failed to flush tables list"); /* XXX: we ignore errors here */ } break; case TOK_SWAP: ac--; av++; NEED1("second table name required"); table_swap(&oh, *av); break; case TOK_LOCK: case TOK_UNLOCK: table_lock(&oh, (tcmd == TOK_LOCK)); break; case TOK_DETAIL: case TOK_INFO: arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL; if (is_all == 0) { if ((error = table_get_info(&oh, &i)) != 0) err(EX_OSERR, "failed to request table info"); table_show_info(&i, arg); } else { error = tables_foreach(table_show_info, arg, 1); if (error != 0) err(EX_OSERR, "failed to request tables list"); } break; case TOK_LIST: arg = is_all ? (void*)1 : NULL; if (is_all == 0) { - ipfw_xtable_info i; if ((error = table_get_info(&oh, &i)) != 0) err(EX_OSERR, "failed to request table info"); table_show_one(&i, arg); } else { error = tables_foreach(table_show_one, arg, 1); if (error != 0) err(EX_OSERR, "failed to request tables list"); } break; case TOK_LOOKUP: ac--; av++; table_lookup(&oh, ac, av); break; } } void table_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set, uint16_t uidx) { ntlv->head.type = IPFW_TLV_TBL_NAME; ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = uidx; ntlv->set = set; strlcpy(ntlv->name, name, sizeof(ntlv->name)); } static void table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i) { oh->idx = 1; table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1); } static struct _s_x tablenewcmds[] = { { "type", TOK_TYPE }, { "valtype", TOK_VALTYPE }, { "algo", TOK_ALGO }, { "limit", TOK_LIMIT }, { "locked", TOK_LOCK }, { "missing", TOK_MISSING }, { "or-flush", TOK_ORFLUSH }, { NULL, 0 } }; static struct _s_x flowtypecmds[] = { { "src-ip", IPFW_TFFLAG_SRCIP }, { "proto", IPFW_TFFLAG_PROTO }, { "src-port", IPFW_TFFLAG_SRCPORT }, { "dst-ip", IPFW_TFFLAG_DSTIP }, { "dst-port", IPFW_TFFLAG_DSTPORT }, { NULL, 0 } }; -int +static int table_parse_type(uint8_t ttype, char *p, uint8_t *tflags) { uint32_t fset, fclear; char *e; /* Parse type options */ switch(ttype) { case IPFW_TABLE_FLOW: fset = fclear = 0; if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0) errx(EX_USAGE, "unable to parse flow option %s", e); *tflags = fset; break; default: return (EX_USAGE); } return (0); } -void +static void table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags) { const char *tname; int l; if ((tname = match_value(tabletypes, type)) == NULL) tname = "unknown"; l = snprintf(tbuf, size, "%s", tname); tbuf += l; size -= l; switch(type) { case IPFW_TABLE_FLOW: if (tflags != 0) { *tbuf++ = ':'; l--; print_flags_buffer(tbuf, size, flowtypecmds, tflags); } break; } } /* * Creates new table * * ipfw table NAME create [ type { addr | iface | number | flow } ] * [ algo algoname ] [missing] [or-flush] */ static void table_create(ipfw_obj_header *oh, int ac, char *av[]) { ipfw_xtable_info xi, xie; int error, missing, orflush, tcmd, val; uint32_t fset, fclear; char *e, *p; char tbuf[128]; missing = orflush = 0; memset(&xi, 0, sizeof(xi)); while (ac > 0) { tcmd = get_token(tablenewcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_LIMIT: NEED1("limit value required"); xi.limit = strtol(*av, NULL, 10); ac--; av++; break; case TOK_TYPE: NEED1("table type required"); /* Type may have suboptions after ':' */ if ((p = strchr(*av, ':')) != NULL) *p++ = '\0'; val = match_token(tabletypes, *av); if (val == -1) { concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", "); errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s", *av, tbuf); } xi.type = val; if (p != NULL) { error = table_parse_type(val, p, &xi.tflags); if (error != 0) errx(EX_USAGE, "Unsupported suboptions: %s", p); } ac--; av++; break; case TOK_VALTYPE: NEED1("table value type required"); fset = fclear = 0; val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear); if (val != -1) { xi.vmask = fset; ac--; av++; break; } concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", "); errx(EX_USAGE, "Unknown value type: %s. Supported: %s", e, tbuf); break; case TOK_ALGO: NEED1("table algorithm name required"); if (strlen(*av) > sizeof(xi.algoname)) errx(EX_USAGE, "algorithm name too long"); strlcpy(xi.algoname, *av, sizeof(xi.algoname)); ac--; av++; break; case TOK_LOCK: xi.flags |= IPFW_TGFLAGS_LOCKED; break; case TOK_ORFLUSH: orflush = 1; /* FALLTHROUGH */ case TOK_MISSING: missing = 1; break; } } /* Set some defaults to preserve compatibility. */ if (xi.algoname[0] == '\0' && xi.type == 0) xi.type = IPFW_TABLE_ADDR; if (xi.vmask == 0) xi.vmask = IPFW_VTYPE_LEGACY; error = table_do_create(oh, &xi); if (error == 0) return; if (errno != EEXIST || missing == 0) err(EX_OSERR, "Table creation failed"); /* Check that existing table is the same we are trying to create */ if (table_get_info(oh, &xie) != 0) err(EX_OSERR, "Existing table check failed"); if (xi.limit != xie.limit || xi.type != xie.type || xi.tflags != xie.tflags || xi.vmask != xie.vmask || ( xi.algoname[0] != '\0' && strcmp(xi.algoname, xie.algoname) != 0) || xi.flags != xie.flags) errx(EX_DATAERR, "The existing table is not compatible " "with one you are creating."); /* Flush existing table if instructed to do so */ if (orflush != 0 && table_flush(oh) != 0) err(EX_OSERR, "Table flush on creation failed"); } /* * Creates new table * * Request: [ ipfw_obj_header ipfw_xtable_info ] * * Returns 0 on success. */ static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; int error; memcpy(tbuf, oh, sizeof(*oh)); memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); oh = (ipfw_obj_header *)tbuf; error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf)); return (error); } /* * Modifies existing table * * ipfw table NAME modify [ limit number ] */ static void table_modify(ipfw_obj_header *oh, int ac, char *av[]) { ipfw_xtable_info xi; int tcmd; memset(&xi, 0, sizeof(xi)); while (ac > 0) { tcmd = get_token(tablenewcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_LIMIT: NEED1("limit value required"); xi.limit = strtol(*av, NULL, 10); xi.mflags |= IPFW_TMFLAGS_LIMIT; ac--; av++; break; default: errx(EX_USAGE, "cmd is not supported for modification"); } } if (table_do_modify(oh, &xi) != 0) err(EX_OSERR, "Table modification failed"); } /* * Modifies existing table. * * Request: [ ipfw_obj_header ipfw_xtable_info ] * * Returns 0 on success. */ static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; int error; memcpy(tbuf, oh, sizeof(*oh)); memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); oh = (ipfw_obj_header *)tbuf; error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf)); return (error); } /* * Locks or unlocks given table */ static void table_lock(ipfw_obj_header *oh, int lock) { ipfw_xtable_info xi; memset(&xi, 0, sizeof(xi)); xi.mflags |= IPFW_TMFLAGS_LOCK; xi.flags |= (lock != 0) ? IPFW_TGFLAGS_LOCKED : 0; if (table_do_modify(oh, &xi) != 0) err(EX_OSERR, "Table %s failed", lock != 0 ? "lock" : "unlock"); } /* * Destroys given table specified by @oh->ntlv. * Returns 0 on success. */ static int table_destroy(ipfw_obj_header *oh) { if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0) return (-1); return (0); } static int table_destroy_one(ipfw_xtable_info *i, void *arg) { ipfw_obj_header *oh; oh = (ipfw_obj_header *)arg; table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1); if (table_destroy(oh) != 0) { - if (co.do_quiet == 0) + if (g_co.do_quiet == 0) warn("failed to destroy table(%s) in set %u", i->tablename, i->set); return (-1); } return (0); } /* * Flushes given table specified by @oh->ntlv. * Returns 0 on success. */ static int table_flush(ipfw_obj_header *oh) { if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0) return (-1); return (0); } static int table_do_swap(ipfw_obj_header *oh, char *second) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)]; int error; memset(tbuf, 0, sizeof(tbuf)); memcpy(tbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)tbuf; table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1); error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf)); return (error); } /* * Swaps given table with @second one. */ static int table_swap(ipfw_obj_header *oh, char *second) { if (table_check_name(second) != 0) errx(EX_USAGE, "table name %s is invalid", second); if (table_do_swap(oh, second) == 0) return (0); switch (errno) { case EINVAL: errx(EX_USAGE, "Unable to swap table: check types"); case EFBIG: errx(EX_USAGE, "Unable to swap table: check limits"); } return (0); } /* * Retrieves table in given table specified by @oh->ntlv. * it inside @i. * Returns 0 on success. */ static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; size_t sz; sz = sizeof(tbuf); memset(tbuf, 0, sizeof(tbuf)); memcpy(tbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)tbuf; if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) != 0) return (errno); if (sz < sizeof(tbuf)) return (EINVAL); *i = *(ipfw_xtable_info *)(oh + 1); return (0); } static struct _s_x tablealgoclass[] = { { "hash", IPFW_TACLASS_HASH }, { "array", IPFW_TACLASS_ARRAY }, { "radix", IPFW_TACLASS_RADIX }, { NULL, 0 } }; struct ta_cldata { uint8_t taclass; uint8_t spare4; uint16_t itemsize; uint16_t itemsize6; uint32_t size; uint32_t count; }; /* * Print global/per-AF table @i algorithm info. */ static void -table_show_tainfo(ipfw_xtable_info *i, struct ta_cldata *d, +table_show_tainfo(ipfw_xtable_info *i __unused, struct ta_cldata *d, const char *af, const char *taclass) { switch (d->taclass) { case IPFW_TACLASS_HASH: case IPFW_TACLASS_ARRAY: printf(" %salgorithm %s info\n", af, taclass); if (d->itemsize == d->itemsize6) printf(" size: %u items: %u itemsize: %u\n", d->size, d->count, d->itemsize); else printf(" size: %u items: %u " "itemsize4: %u itemsize6: %u\n", d->size, d->count, d->itemsize, d->itemsize6); break; case IPFW_TACLASS_RADIX: printf(" %salgorithm %s info\n", af, taclass); if (d->itemsize == d->itemsize6) printf(" items: %u itemsize: %u\n", d->count, d->itemsize); else printf(" items: %u " "itemsize4: %u itemsize6: %u\n", d->count, d->itemsize, d->itemsize6); break; default: printf(" algo class: %s\n", taclass); } } static void table_print_valheader(char *buf, size_t bufsize, uint32_t vmask) { if (vmask == IPFW_VTYPE_LEGACY) { snprintf(buf, bufsize, "legacy"); return; } memset(buf, 0, bufsize); print_flags_buffer(buf, bufsize, tablevaltypes, vmask); } /* * Prints table info struct @i in human-readable form. */ static int table_show_info(ipfw_xtable_info *i, void *arg) { const char *vtype; ipfw_ta_tinfo *tainfo; int afdata, afitem; struct ta_cldata d; char ttype[64], tvtype[64]; table_print_type(ttype, sizeof(ttype), i->type, i->tflags); table_print_valheader(tvtype, sizeof(tvtype), i->vmask); printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0) printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype); else printf(" kindex: %d, type: %s\n", i->kidx, ttype); printf(" references: %u, valtype: %s\n", i->refcnt, tvtype); printf(" algorithm: %s\n", i->algoname); printf(" items: %u, size: %u\n", i->count, i->size); if (i->limit > 0) printf(" limit: %u\n", i->limit); /* Print algo-specific info if requested & set */ if (arg == NULL) return (0); if ((i->ta_info.flags & IPFW_TATFLAGS_DATA) == 0) return (0); tainfo = &i->ta_info; afdata = 0; afitem = 0; if (tainfo->flags & IPFW_TATFLAGS_AFDATA) afdata = 1; if (tainfo->flags & IPFW_TATFLAGS_AFITEM) afitem = 1; memset(&d, 0, sizeof(d)); d.taclass = tainfo->taclass4; d.size = tainfo->size4; d.count = tainfo->count4; d.itemsize = tainfo->itemsize4; if (afdata == 0 && afitem != 0) d.itemsize6 = tainfo->itemsize6; else d.itemsize6 = d.itemsize; if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL) vtype = "unknown"; if (afdata == 0) { table_show_tainfo(i, &d, "", vtype); } else { table_show_tainfo(i, &d, "IPv4 ", vtype); memset(&d, 0, sizeof(d)); d.taclass = tainfo->taclass6; if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL) vtype = "unknown"; d.size = tainfo->size6; d.count = tainfo->count6; d.itemsize = tainfo->itemsize6; d.itemsize6 = d.itemsize; table_show_tainfo(i, &d, "IPv6 ", vtype); } return (0); } /* * Function wrappers which can be used either * as is or as foreach function parameter. */ static int table_show_one(ipfw_xtable_info *i, void *arg) { ipfw_obj_header *oh; int error; int is_all; is_all = arg == NULL ? 0 : 1; if ((error = table_do_get_list(i, &oh)) != 0) { err(EX_OSERR, "Error requesting table %s list", i->tablename); return (error); } table_show_list(oh, is_all); free(oh); return (0); } static int table_flush_one(ipfw_xtable_info *i, void *arg) { ipfw_obj_header *oh; oh = (ipfw_obj_header *)arg; table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1); return (table_flush(oh)); } static int table_do_modify_record(int cmd, ipfw_obj_header *oh, ipfw_obj_tentry *tent, int count, int atomic) { ipfw_obj_ctlv *ctlv; ipfw_obj_tentry *tent_base; caddr_t pbuf; char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)]; int error, i; size_t sz; sz = sizeof(*ctlv) + sizeof(*tent) * count; if (count == 1) { memset(xbuf, 0, sizeof(xbuf)); pbuf = xbuf; } else { if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL) return (ENOMEM); } memcpy(pbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)pbuf; oh->opheader.version = 1; ctlv = (ipfw_obj_ctlv *)(oh + 1); ctlv->count = count; ctlv->head.length = sz; if (atomic != 0) ctlv->flags |= IPFW_CTF_ATOMIC; tent_base = tent; memcpy(ctlv + 1, tent, sizeof(*tent) * count); tent = (ipfw_obj_tentry *)(ctlv + 1); for (i = 0; i < count; i++, tent++) { tent->head.length = sizeof(ipfw_obj_tentry); tent->idx = oh->idx; } sz += sizeof(*oh); error = do_get3(cmd, &oh->opheader, &sz); if (error != 0) error = errno; tent = (ipfw_obj_tentry *)(ctlv + 1); /* Copy result back to provided buffer */ memcpy(tent_base, ctlv + 1, sizeof(*tent) * count); if (pbuf != xbuf) free(pbuf); return (error); } static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int quiet, int update, int atomic) { ipfw_obj_tentry *ptent, tent, *tent_buf; ipfw_xtable_info xi; + const char *etxt, *px, *texterr; uint8_t type; uint32_t vmask; int cmd, count, error, i, ignored; - char *texterr, *etxt, *px; if (ac == 0) errx(EX_USAGE, "address required"); if (add != 0) { cmd = IP_FW_TABLE_XADD; texterr = "Adding record failed"; } else { cmd = IP_FW_TABLE_XDEL; texterr = "Deleting record failed"; } /* * Calculate number of entries: * Assume [key val] x N for add * and * key x N for delete */ count = (add != 0) ? ac / 2 + 1 : ac; if (count <= 1) { /* Adding single entry with/without value */ memset(&tent, 0, sizeof(tent)); tent_buf = &tent; } else { if ((tent_buf = calloc(count, sizeof(tent))) == NULL) errx(EX_OSERR, "Unable to allocate memory for all entries"); } ptent = tent_buf; memset(&xi, 0, sizeof(xi)); count = 0; while (ac > 0) { tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi); /* * Compatibility layer: auto-create table if not exists. */ if (xi.tablename[0] == '\0') { xi.type = type; xi.vmask = vmask; strlcpy(xi.tablename, oh->ntlv.name, sizeof(xi.tablename)); if (quiet == 0) warnx("DEPRECATED: inserting data into " "non-existent table %s. (auto-created)", xi.tablename); table_do_create(oh, &xi); } oh->ntlv.type = type; ac--; av++; if (add != 0 && ac > 0) { tentry_fill_value(oh, ptent, *av, type, vmask); ac--; av++; } if (update != 0) ptent->head.flags |= IPFW_TF_UPDATE; count++; ptent++; } error = table_do_modify_record(cmd, oh, tent_buf, count, atomic); /* * Compatibility stuff: do not yell on duplicate keys or * failed deletions. */ if (error == 0 || (error == EEXIST && add != 0) || (error == ENOENT && add == 0)) { if (quiet != 0) { if (tent_buf != &tent) free(tent_buf); return; } } /* Report results back */ ptent = tent_buf; for (i = 0; i < count; ptent++, i++) { ignored = 0; switch (ptent->result) { case IPFW_TR_ADDED: px = "added"; break; case IPFW_TR_DELETED: px = "deleted"; break; case IPFW_TR_UPDATED: px = "updated"; break; case IPFW_TR_LIMIT: px = "limit"; ignored = 1; break; case IPFW_TR_ERROR: px = "error"; ignored = 1; break; case IPFW_TR_NOTFOUND: px = "notfound"; ignored = 1; break; case IPFW_TR_EXISTS: px = "exists"; ignored = 1; break; case IPFW_TR_IGNORED: px = "ignored"; ignored = 1; break; default: px = "unknown"; ignored = 1; } if (error != 0 && atomic != 0 && ignored == 0) printf("%s(reverted): ", px); else printf("%s: ", px); table_show_entry(&xi, ptent); } if (tent_buf != &tent) free(tent_buf); if (error == 0) return; /* Get real OS error */ error = errno; /* Try to provide more human-readable error */ switch (error) { case EEXIST: etxt = "record already exists"; break; case EFBIG: etxt = "limit hit"; break; case ESRCH: etxt = "table not found"; break; case ENOENT: etxt = "record not found"; break; case EACCES: etxt = "table is locked"; break; default: etxt = strerror(error); } errx(EX_OSERR, "%s: %s", texterr, etxt); } static int table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi, ipfw_obj_tentry *xtent) { char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)]; ipfw_obj_tentry *tent; uint8_t type; uint32_t vmask; size_t sz; memcpy(xbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)xbuf; tent = (ipfw_obj_tentry *)(oh + 1); memset(tent, 0, sizeof(*tent)); tent->head.length = sizeof(*tent); tent->idx = 1; tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi); oh->ntlv.type = type; sz = sizeof(xbuf); if (do_get3(IP_FW_TABLE_XFIND, &oh->opheader, &sz) != 0) return (errno); if (sz < sizeof(xbuf)) return (EINVAL); *xtent = *tent; return (0); } static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]) { ipfw_obj_tentry xtent; ipfw_xtable_info xi; char key[64]; int error; if (ac == 0) errx(EX_USAGE, "address required"); strlcpy(key, *av, sizeof(key)); memset(&xi, 0, sizeof(xi)); error = table_do_lookup(oh, key, &xi, &xtent); switch (error) { case 0: break; case ESRCH: errx(EX_UNAVAILABLE, "Table %s not found", oh->ntlv.name); case ENOENT: errx(EX_UNAVAILABLE, "Entry %s not found", *av); case ENOTSUP: errx(EX_UNAVAILABLE, "Table %s algo does not support " "\"lookup\" method", oh->ntlv.name); default: err(EX_OSERR, "getsockopt(IP_FW_TABLE_XFIND)"); } table_show_entry(&xi, &xtent); } static void tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type, uint8_t tflags) { char *p, *pp; int mask, af; struct in6_addr *paddr, tmp; struct tflow_entry *tfe; uint32_t key, *pkey; uint16_t port; struct protoent *pent; struct servent *sent; int masklen; masklen = 0; af = 0; paddr = (struct in6_addr *)&tentry->k; switch (type) { case IPFW_TABLE_ADDR: /* Remove / if exists */ if ((p = strchr(arg, '/')) != NULL) { *p = '\0'; mask = atoi(p + 1); } if (inet_pton(AF_INET, arg, paddr) == 1) { if (p != NULL && mask > 32) errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); masklen = p ? mask : 32; af = AF_INET; } else if (inet_pton(AF_INET6, arg, paddr) == 1) { if (IN6_IS_ADDR_V4COMPAT(paddr)) errx(EX_DATAERR, "Use IPv4 instead of v4-compatible"); if (p != NULL && mask > 128) errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); masklen = p ? mask : 128; af = AF_INET6; } else { /* Assume FQDN */ if (lookup_host(arg, (struct in_addr *)paddr) != 0) errx(EX_NOHOST, "hostname ``%s'' unknown", arg); masklen = 32; type = IPFW_TABLE_ADDR; af = AF_INET; } break; case IPFW_TABLE_INTERFACE: /* Assume interface name. Copy significant data only */ mask = MIN(strlen(arg), IF_NAMESIZE - 1); memcpy(paddr, arg, mask); /* Set mask to exact match */ masklen = 8 * IF_NAMESIZE; break; case IPFW_TABLE_NUMBER: /* Port or any other key */ key = strtol(arg, &p, 10); if (*p != '\0') errx(EX_DATAERR, "Invalid number: %s", arg); pkey = (uint32_t *)paddr; *pkey = key; masklen = 32; break; case IPFW_TABLE_FLOW: /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */ tfe = &tentry->k.flow; af = 0; /* Handle */ if ((tflags & IPFW_TFFLAG_SRCIP) != 0) { if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; /* Determine family using temporary storage */ if (inet_pton(AF_INET, arg, &tmp) == 1) { if (af != 0 && af != AF_INET) errx(EX_DATAERR, "Inconsistent address family\n"); af = AF_INET; memcpy(&tfe->a.a4.sip, &tmp, 4); } else if (inet_pton(AF_INET6, arg, &tmp) == 1) { if (af != 0 && af != AF_INET6) errx(EX_DATAERR, "Inconsistent address family\n"); af = AF_INET6; memcpy(&tfe->a.a6.sip6, &tmp, 16); } arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_PROTO) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: proto missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; key = strtol(arg, &pp, 10); if (*pp != '\0') { if ((pent = getprotobyname(arg)) == NULL) errx(EX_DATAERR, "Unknown proto: %s", arg); else key = pent->p_proto; } if (key > 255) errx(EX_DATAERR, "Bad protocol number: %u",key); tfe->proto = key; arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: src port missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; port = htons(strtol(arg, &pp, 10)); if (*pp != '\0') { if ((sent = getservbyname(arg, NULL)) == NULL) errx(EX_DATAERR, "Unknown service: %s", arg); port = sent->s_port; } tfe->sport = port; arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_DSTIP) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: dst ip missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; /* Determine family using temporary storage */ if (inet_pton(AF_INET, arg, &tmp) == 1) { if (af != 0 && af != AF_INET) errx(EX_DATAERR, "Inconsistent address family"); af = AF_INET; memcpy(&tfe->a.a4.dip, &tmp, 4); } else if (inet_pton(AF_INET6, arg, &tmp) == 1) { if (af != 0 && af != AF_INET6) errx(EX_DATAERR, "Inconsistent address family"); af = AF_INET6; memcpy(&tfe->a.a6.dip6, &tmp, 16); } arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: dst port missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; port = htons(strtol(arg, &pp, 10)); if (*pp != '\0') { if ((sent = getservbyname(arg, NULL)) == NULL) errx(EX_DATAERR, "Unknown service: %s", arg); port = sent->s_port; } tfe->dport = port; arg = p; } tfe->af = af; break; default: errx(EX_DATAERR, "Unsupported table type: %d", type); } tentry->subtype = af; tentry->masklen = masklen; } /* * Tries to guess table key type. * This procedure is used in legacy table auto-create * code AND in `ipfw -n` ruleset checking. * * Imported from old table_fill_xentry() parse code. */ static int guess_key_type(char *key, uint8_t *ptype) { char *p; struct in6_addr addr; uint32_t kv; if (ishexnumber(*key) != 0 || *key == ':') { /* Remove / if exists */ if ((p = strchr(key, '/')) != NULL) *p = '\0'; if ((inet_pton(AF_INET, key, &addr) == 1) || (inet_pton(AF_INET6, key, &addr) == 1)) { *ptype = IPFW_TABLE_CIDR; if (p != NULL) *p = '/'; return (0); } else { /* Port or any other key */ /* Skip non-base 10 entries like 'fa1' */ kv = strtol(key, &p, 10); if (*p == '\0') { *ptype = IPFW_TABLE_NUMBER; return (0); } else if ((p != key) && (*p == '.')) { /* * Warn on IPv4 address strings * which are "valid" for inet_aton() but not * in inet_pton(). * * Typical examples: '10.5' or '10.0.0.05' */ return (1); } } } if (strchr(key, '.') == NULL) { *ptype = IPFW_TABLE_INTERFACE; return (0); } if (lookup_host(key, (struct in_addr *)&addr) != 0) return (1); *ptype = IPFW_TABLE_CIDR; return (0); } static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi) { uint8_t type, tflags; uint32_t vmask; int error; type = 0; tflags = 0; vmask = 0; if (xi->tablename[0] == '\0') error = table_get_info(oh, xi); else error = 0; if (error == 0) { - if (co.test_only == 0) { + if (g_co.test_only == 0) { /* Table found */ type = xi->type; tflags = xi->tflags; vmask = xi->vmask; } else { /* * We're running `ipfw -n` * Compatibility layer: try to guess key type * before failing. */ if (guess_key_type(key, &type) != 0) { /* Inknown key */ errx(EX_USAGE, "Cannot guess " "key '%s' type", key); } vmask = IPFW_VTYPE_LEGACY; } } else { if (error != ESRCH) errx(EX_OSERR, "Error requesting table %s info", oh->ntlv.name); if (add == 0) errx(EX_DATAERR, "Table %s does not exist", oh->ntlv.name); /* * Table does not exist * Compatibility layer: try to guess key type before failing. */ if (guess_key_type(key, &type) != 0) { /* Inknown key */ errx(EX_USAGE, "Table %s does not exist, cannot guess " "key '%s' type", oh->ntlv.name, key); } vmask = IPFW_VTYPE_LEGACY; } tentry_fill_key_type(key, tent, type, tflags); *ptype = type; *pvmask = vmask; } static void set_legacy_value(uint32_t val, ipfw_table_value *v) { v->tag = val; v->pipe = val; v->divert = val; v->skipto = val; v->netgraph = val; v->fib = val; v->nat = val; v->nh4 = val; v->dscp = (uint8_t)val; v->limit = val; } static void -tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, - uint8_t type, uint32_t vmask) +tentry_fill_value(ipfw_obj_header *oh __unused, ipfw_obj_tentry *tent, + char *arg, uint8_t type __unused, uint32_t vmask) { struct addrinfo hints, *res; + struct in_addr ipaddr; + const char *etype; + char *comma, *e, *n, *p; uint32_t a4, flag, val; ipfw_table_value *v; uint32_t i; int dval; - char *comma, *e, *etype, *n, *p; - struct in_addr ipaddr; v = &tent->v.value; /* Compat layer: keep old behavior for legacy value types */ if (vmask == IPFW_VTYPE_LEGACY) { /* Try to interpret as number first */ val = strtoul(arg, &p, 0); if (*p == '\0') { set_legacy_value(val, v); return; } if (inet_pton(AF_INET, arg, &val) == 1) { set_legacy_value(ntohl(val), v); return; } /* Try hostname */ if (lookup_host(arg, &ipaddr) == 0) { set_legacy_value(ntohl(ipaddr.s_addr), v); return; } errx(EX_OSERR, "Unable to parse value %s", arg); } /* * Shorthands: handle single value if vmask consists * of numbers only. e.g.: * vmask = "fib,skipto" -> treat input "1" as "1,1" */ n = arg; etype = NULL; - for (i = 1; i < (1 << 31); i *= 2) { + for (i = 1; i < (1u << 31); i *= 2) { if ((flag = (vmask & i)) == 0) continue; vmask &= ~flag; if ((comma = strchr(n, ',')) != NULL) *comma = '\0'; switch (flag) { case IPFW_VTYPE_TAG: v->tag = strtol(n, &e, 10); if (*e != '\0') etype = "tag"; break; case IPFW_VTYPE_PIPE: v->pipe = strtol(n, &e, 10); if (*e != '\0') etype = "pipe"; break; case IPFW_VTYPE_DIVERT: v->divert = strtol(n, &e, 10); if (*e != '\0') etype = "divert"; break; case IPFW_VTYPE_SKIPTO: v->skipto = strtol(n, &e, 10); if (*e != '\0') etype = "skipto"; break; case IPFW_VTYPE_NETGRAPH: v->netgraph = strtol(n, &e, 10); if (*e != '\0') etype = "netgraph"; break; case IPFW_VTYPE_FIB: v->fib = strtol(n, &e, 10); if (*e != '\0') etype = "fib"; break; case IPFW_VTYPE_NAT: v->nat = strtol(n, &e, 10); if (*e != '\0') etype = "nat"; break; case IPFW_VTYPE_LIMIT: v->limit = strtol(n, &e, 10); if (*e != '\0') etype = "limit"; break; case IPFW_VTYPE_NH4: if (strchr(n, '.') != NULL && inet_pton(AF_INET, n, &a4) == 1) { v->nh4 = ntohl(a4); break; } if (lookup_host(n, &ipaddr) == 0) { v->nh4 = ntohl(ipaddr.s_addr); break; } etype = "ipv4"; break; case IPFW_VTYPE_DSCP: if (isalpha(*n)) { if ((dval = match_token(f_ipdscp, n)) != -1) { v->dscp = dval; break; } else etype = "DSCP code"; } else { v->dscp = strtol(n, &e, 10); if (v->dscp > 63 || *e != '\0') etype = "DSCP value"; } break; case IPFW_VTYPE_NH6: if (strchr(n, ':') != NULL) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(n, NULL, &hints, &res) == 0) { v->nh6 = ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; v->zoneid = ((struct sockaddr_in6 *) res->ai_addr)->sin6_scope_id; freeaddrinfo(res); break; } } etype = "ipv6"; break; } if (etype != NULL) errx(EX_USAGE, "Unable to parse %s as %s", n, etype); if (comma != NULL) *comma++ = ','; if ((n = comma) != NULL) continue; /* End of input. */ if (vmask != 0) errx(EX_USAGE, "Not enough fields inside value"); } } /* * Compare table names. * Honor number comparison. */ static int tablename_cmp(const void *a, const void *b) { - ipfw_xtable_info *ia, *ib; + const ipfw_xtable_info *ia, *ib; - ia = (ipfw_xtable_info *)a; - ib = (ipfw_xtable_info *)b; + ia = (const ipfw_xtable_info *)a; + ib = (const ipfw_xtable_info *)b; return (stringnum_cmp(ia->tablename, ib->tablename)); } /* * Retrieves table list from kernel, * optionally sorts it and calls requested function for each table. * Returns 0 on success. */ static int tables_foreach(table_cb_t *f, void *arg, int sort) { ipfw_obj_lheader *olh; ipfw_xtable_info *info; size_t sz; - int i, error; + uint32_t i; + int error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(ipfw_xtable_info); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_TABLES_XLIST, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno != ENOMEM) return (errno); continue; } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, tablename_cmp); info = (ipfw_xtable_info *)(olh + 1); for (i = 0; i < olh->count; i++) { - if (co.use_set == 0 || info->set == co.use_set - 1) + if (g_co.use_set == 0 || info->set == g_co.use_set - 1) error = f(info, arg); info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize); } free(olh); break; } return (0); } /* * Retrieves all entries for given table @i in * eXtended format. Allocate buffer large enough * to store result. Called needs to free it later. * * Returns 0 on success. */ static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh) { ipfw_obj_header *oh; size_t sz; int c; sz = 0; oh = NULL; for (c = 0; c < 8; c++) { if (sz < i->size) sz = i->size + 44; if (oh != NULL) free(oh); if ((oh = calloc(1, sz)) == NULL) continue; table_fill_objheader(oh, i); oh->opheader.version = 1; /* Current version */ if (do_get3(IP_FW_TABLE_XLIST, &oh->opheader, &sz) == 0) { *poh = oh; return (0); } if (errno != ENOMEM) break; } free(oh); return (errno); } /* * Shows all entries from @oh in human-readable format */ static void table_show_list(ipfw_obj_header *oh, int need_header) { ipfw_obj_tentry *tent; uint32_t count; ipfw_xtable_info *i; i = (ipfw_xtable_info *)(oh + 1); tent = (ipfw_obj_tentry *)(i + 1); if (need_header) printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); count = i->count; while (count > 0) { table_show_entry(i, tent); tent = (ipfw_obj_tentry *)((caddr_t)tent + tent->head.length); count--; } } static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v, uint32_t vmask, int print_ip) { char abuf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2]; struct sockaddr_in6 sa6; uint32_t flag, i, l; size_t sz; struct in_addr a4; sz = bufsize; /* * Some shorthands for printing values: * legacy assumes all values are equal, so keep the first one. */ if (vmask == IPFW_VTYPE_LEGACY) { if (print_ip != 0) { flag = htonl(v->tag); inet_ntop(AF_INET, &flag, buf, sz); } else snprintf(buf, sz, "%u", v->tag); return; } - for (i = 1; i < (1 << 31); i *= 2) { + for (i = 1; i < (1u << 31); i *= 2) { if ((flag = (vmask & i)) == 0) continue; l = 0; switch (flag) { case IPFW_VTYPE_TAG: l = snprintf(buf, sz, "%u,", v->tag); break; case IPFW_VTYPE_PIPE: l = snprintf(buf, sz, "%u,", v->pipe); break; case IPFW_VTYPE_DIVERT: l = snprintf(buf, sz, "%d,", v->divert); break; case IPFW_VTYPE_SKIPTO: l = snprintf(buf, sz, "%d,", v->skipto); break; case IPFW_VTYPE_NETGRAPH: l = snprintf(buf, sz, "%u,", v->netgraph); break; case IPFW_VTYPE_FIB: l = snprintf(buf, sz, "%u,", v->fib); break; case IPFW_VTYPE_NAT: l = snprintf(buf, sz, "%u,", v->nat); break; case IPFW_VTYPE_LIMIT: l = snprintf(buf, sz, "%u,", v->limit); break; case IPFW_VTYPE_NH4: a4.s_addr = htonl(v->nh4); inet_ntop(AF_INET, &a4, abuf, sizeof(abuf)); l = snprintf(buf, sz, "%s,", abuf); break; case IPFW_VTYPE_DSCP: l = snprintf(buf, sz, "%d,", v->dscp); break; case IPFW_VTYPE_NH6: sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); sa6.sin6_addr = v->nh6; sa6.sin6_port = 0; sa6.sin6_scope_id = v->zoneid; if (getnameinfo((const struct sockaddr *)&sa6, sa6.sin6_len, abuf, sizeof(abuf), NULL, 0, NI_NUMERICHOST) == 0) l = snprintf(buf, sz, "%s,", abuf); break; } buf += l; sz -= l; } if (sz != bufsize) *(buf - 1) = '\0'; } static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent) { - char *comma, tbuf[128], pval[128]; + char tbuf[128], pval[128]; + const char *comma; void *paddr; struct tflow_entry *tfe; table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask, - co.do_value_as_ip); + g_co.do_value_as_ip); switch (i->type) { case IPFW_TABLE_ADDR: /* IPv4 or IPv6 prefixes */ inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf)); printf("%s/%u %s\n", tbuf, tent->masklen, pval); break; case IPFW_TABLE_INTERFACE: /* Interface names */ printf("%s %s\n", tent->k.iface, pval); break; case IPFW_TABLE_NUMBER: /* numbers */ printf("%u %s\n", tent->k.key, pval); break; case IPFW_TABLE_FLOW: /* flows */ tfe = &tent->k.flow; comma = ""; if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) { if (tfe->af == AF_INET) paddr = &tfe->a.a4.sip; else paddr = &tfe->a.a6.sip6; inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf)); printf("%s%s", comma, tbuf); comma = ","; } if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) { printf("%s%d", comma, tfe->proto); comma = ","; } if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) { printf("%s%d", comma, ntohs(tfe->sport)); comma = ","; } if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) { if (tfe->af == AF_INET) paddr = &tfe->a.a4.dip; else paddr = &tfe->a.a6.dip6; inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf)); printf("%s%s", comma, tbuf); comma = ","; } if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) { printf("%s%d", comma, ntohs(tfe->dport)); comma = ","; } printf(" %s\n", pval); } } static int table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh) { ipfw_obj_lheader req, *olh; size_t sz; memset(&req, 0, sizeof(req)); sz = sizeof(req); if (do_get3(opcode, &req.opheader, &sz) != 0) if (errno != ENOMEM) return (errno); sz = req.size; if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(opcode, &olh->opheader, &sz) != 0) { free(olh); return (errno); } *polh = olh; return (0); } static int table_do_get_algolist(ipfw_obj_lheader **polh) { return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh)); } static int table_do_get_vlist(ipfw_obj_lheader **polh) { return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh)); } void -ipfw_list_ta(int ac, char *av[]) +ipfw_list_ta(int ac __unused, char *av[] __unused) { ipfw_obj_lheader *olh; ipfw_ta_info *info; - int error, i; const char *atype; + uint32_t i; + int error; error = table_do_get_algolist(&olh); if (error != 0) err(EX_OSERR, "Unable to request algorithm list"); info = (ipfw_ta_info *)(olh + 1); for (i = 0; i < olh->count; i++) { if ((atype = match_value(tabletypes, info->type)) == NULL) atype = "unknown"; printf("--- %s ---\n", info->algoname); printf(" type: %s\n refcount: %u\n", atype, info->refcnt); info = (ipfw_ta_info *)((caddr_t)info + olh->objsize); } free(olh); } /* Copy of current kernel table_value structure */ struct _table_value { uint32_t tag; /* O_TAG/O_TAGGED */ uint32_t pipe; /* O_PIPE/O_QUEUE */ uint16_t divert; /* O_DIVERT/O_TEE */ uint16_t skipto; /* skipto, CALLRET */ uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */ uint32_t fib; /* O_SETFIB */ uint32_t nat; /* O_NAT */ uint32_t nh4; uint8_t dscp; uint8_t spare0; uint16_t spare1; /* -- 32 bytes -- */ struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ uint32_t zoneid; uint64_t refcnt; /* Number of references */ }; -int +static int compare_values(const void *_a, const void *_b) { - struct _table_value *a, *b; + const struct _table_value *a, *b; - a = (struct _table_value *)_a; - b = (struct _table_value *)_b; + a = (const struct _table_value *)_a; + b = (const struct _table_value *)_b; if (a->spare1 < b->spare1) return (-1); else if (a->spare1 > b->spare1) return (1); return (0); } void -ipfw_list_values(int ac, char *av[]) +ipfw_list_values(int ac __unused, char *av[] __unused) { + char buf[128]; ipfw_obj_lheader *olh; struct _table_value *v; - int error, i; - uint32_t vmask; - char buf[128]; + uint32_t i, vmask; + int error; error = table_do_get_vlist(&olh); if (error != 0) err(EX_OSERR, "Unable to request value list"); vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */ table_print_valheader(buf, sizeof(buf), vmask); printf("HEADER: %s\n", buf); v = (struct _table_value *)(olh + 1); qsort(v, olh->count, olh->objsize, compare_values); for (i = 0; i < olh->count; i++) { table_show_value(buf, sizeof(buf), (ipfw_table_value *)v, vmask, 0); printf("[%u] refs=%lu %s\n", v->spare1, (u_long)v->refcnt, buf); v = (struct _table_value *)((caddr_t)v + olh->objsize); } free(olh); } int table_check_name(const char *tablename) { if (ipfw_check_object_name(tablename) != 0) return (EINVAL); /* Restrict some 'special' names */ if (strcmp(tablename, "all") == 0) return (EINVAL); return (0); }