diff options
Diffstat (limited to 'sys/netpfil/ipfw/ip_fw_sockopt.c')
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_sockopt.c | 330 |
1 files changed, 318 insertions, 12 deletions
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 3c342f7..2776e4c 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -736,8 +736,8 @@ check_ipfw_struct(struct ip_fw *rule, int size) if (!IPFW_NAT_LOADED) return EINVAL; if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) - goto bad_size; - goto check_action; + goto bad_size; + goto check_action; case O_FORWARD_MAC: /* XXX not implemented yet */ case O_CHECK_STATE: case O_COUNT: @@ -943,12 +943,15 @@ ipfw_ctl(struct sockopt *sopt) #define RULE_MAXSIZE (256*sizeof(u_int32_t)) int error; size_t size, len, valsize; + struct ifnet *ifp; struct ip_fw *buf, *rule; - struct ip_fw_chain *chain; + static struct ip_fw_chain *chain; + struct ip_fw_ctx_iflist *tmpifl, *tmpifl2 = NULL; + ip_fw3_opheader *op3 = NULL; u_int32_t rulenum[2]; uint32_t opt; char xbuf[128]; - ip_fw3_opheader *op3 = NULL; + char *ifname; error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); if (error) @@ -965,7 +968,6 @@ ipfw_ctl(struct sockopt *sopt) return (error); } - chain = &V_layer3_chain; error = 0; /* Save original valsize before it is altered via sooptcopyin() */ @@ -980,9 +982,238 @@ ipfw_ctl(struct sockopt *sopt) return (error); op3 = (ip_fw3_opheader *)xbuf; opt = op3->opcode; + + if (op3->ctxid >= IP_FW_MAXCTX) + return (EINVAL); + + if (opt != IP_FW_CTX_GET && opt != IP_FW_CTX_ADD) { + if (op3->ctxid == 0) + return (ENOENT); + + IPFW_CTX_RLOCK(); + chain = V_ip_fw_contexts.chain[op3->ctxid]; + IPFW_CTX_RUNLOCK(); + if (chain == NULL) + return (ENOENT); + } } + /* Verification needed to avoid problems */ switch (opt) { + case IP_FW_CTX_GET: + case IP_FW_CTX_ADD: + case IP_FW_CTX_DEL: + break; + default: + if (chain == NULL) + return (EINVAL); + /* NOTREACHED */ + } + + switch (opt) { + case IP_FW_CTX_ADD: + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] != NULL) { + IPFW_CTX_WUNLOCK(); + return (EEXIST); + } + + chain = malloc(sizeof(struct ip_fw_chain), M_IPFW, M_WAITOK | M_ZERO); + TAILQ_INIT(&V_ip_fw_contexts.iflist[op3->ctxid]); + V_ip_fw_contexts.chain[op3->ctxid] = chain; + ipfw_context_init(op3->ctxid); /* XXX: error checking */ + IPFW_CTX_WUNLOCK(); + break; + + case IP_FW_CTX_DEL: + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) { + IPFW_CTX_WUNLOCK(); + return (ENOENT); + } + + ipfw_context_uninit(op3->ctxid); + V_ip_fw_contexts.chain[op3->ctxid] = NULL; + IPFW_CTX_WUNLOCK(); + break; + + case IP_FW_CTX_GET: + { + int i, n, len = 0, want; + char *bufout, *tmpbuf; + + sopt->sopt_valsize = valsize; + + IPFW_CTX_RLOCK(); + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (op3->ctxid > 0 && op3->ctxid != i) + continue; + if (op3->ctxid > 0 && op3->ctxid < i) + break; + + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + /* Calculate number of bytes for the integer */ + n = i; + while (n > 0) { + n /= 10; + len++; + } + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + len += strlen(tmpifl->ifname) + 1; + } + len += 3; // newline, :, space + } + IPFW_CTX_RUNLOCK(); + + if (len > sopt->sopt_valsize) { + sopt->sopt_valsize = len; + break; + } + + bufout = malloc(len, M_TEMP, M_WAITOK | M_ZERO); + if (bufout == NULL) + break; + + /* Record our size for later checks */ + want = len; + len = 0; + IPFW_CTX_RLOCK(); + /* Recalculate length to detect if smth changed */ + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (op3->ctxid > 0 && op3->ctxid != i) + continue; + if (op3->ctxid > 0 && op3->ctxid < i) + break; + + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + /* Calculate number of bytes for the integer */ + n = i; + while (n > 0) { + n /= 10; + len++; + } + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + len += strlen(tmpifl->ifname) + 1; + } + len += 3; // newline, :, space + } + + if (want >= len) { + tmpbuf = bufout; + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (op3->ctxid > 0 && op3->ctxid != i) + continue; + if (op3->ctxid > 0 && op3->ctxid < i) + break; + + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + sprintf(tmpbuf, "%d: ", i); + tmpbuf += strlen(tmpbuf); + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + sprintf(tmpbuf, "%s,", tmpifl->ifname); + tmpbuf += strlen(tmpifl->ifname) + 1; + } + sprintf(tmpbuf, "\n"); + tmpbuf++; + } + } + IPFW_CTX_RUNLOCK(); + + if (want >= len) + error = sooptcopyout(sopt, bufout, len); + else + len = 0; + free(bufout, M_TEMP); + } + break; + + case IP_FW_CTX_SET: + /* XXX: Maybe not use this option at all? */ + IPFW_CTX_RLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) + error = ENOENT; + else + chain = V_ip_fw_contexts.chain[op3->ctxid]; + IPFW_CTX_RUNLOCK(); + break; + + case IP_FW_CTX_ADDMEMBER: + { + int i; + + ifname = (char *)(op3 + 1); + ifp = ifunit(ifname); + if (ifp == NULL) + return (ENOENT); + + tmpifl = malloc(sizeof(*tmpifl), M_IPFW, M_WAITOK | M_ZERO); + + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) { + IPFW_CTX_WUNLOCK(); + free(tmpifl, M_IPFW); + return (ENOENT); + } + + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + TAILQ_FOREACH(tmpifl2, &V_ip_fw_contexts.iflist[i], entry) { + if (strlen(tmpifl2->ifname) != strlen(ifname)) + continue; + if (!strcmp(tmpifl2->ifname, ifname)) + goto ctxifacefound; + } + } +ctxifacefound: + if (tmpifl2 != NULL) { + IPFW_CTX_WUNLOCK(); + free(tmpifl, M_IPFW); + return (EEXIST); + } + + strlcpy(tmpifl->ifname, ifname, IFNAMSIZ); + TAILQ_INSERT_HEAD(&V_ip_fw_contexts.iflist[op3->ctxid], tmpifl, entry); + ifp->if_ispare[0] = op3->ctxid; + IPFW_CTX_WUNLOCK(); + } + break; + + case IP_FW_CTX_DELMEMBER: + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) { + IPFW_CTX_WUNLOCK(); + return (ENOENT); + } + + ifname = (char *)(op3 + 1); + TAILQ_FOREACH(tmpifl2, &V_ip_fw_contexts.iflist[op3->ctxid], entry) { + if (strlen(tmpifl2->ifname) != strlen(ifname)) + continue; + if (!strcmp(tmpifl2->ifname, ifname)) + break; + } + if (tmpifl2 == NULL) { + IPFW_CTX_WUNLOCK(); + return (ENOENT); + } + + TAILQ_REMOVE(&V_ip_fw_contexts.iflist[op3->ctxid], tmpifl2, entry); + IPFW_CTX_WUNLOCK(); + free(tmpifl2, M_IPFW); + + ifp = ifunit(ifname); + if (ifp != NULL) + ifp->if_ispare[0] = 0; + break; + case IP_FW_GET: /* * pass up a copy of the current rules. Static rules @@ -1124,7 +1355,7 @@ ipfw_ctl(struct sockopt *sopt) break; error = ipfw_add_table_entry(chain, ent.tbl, &ent.addr, sizeof(ent.addr), ent.masklen, - IPFW_TABLE_CIDR, ent.value); + IPFW_TABLE_CIDR, ent.mac_addr, ent.value); } break; @@ -1157,12 +1388,12 @@ ipfw_ctl(struct sockopt *sopt) error = EINVAL; break; } - + len = xent->len - offsetof(ipfw_table_xentry, k); error = (opt == IP_FW_TABLE_XADD) ? ipfw_add_table_entry(chain, xent->tbl, &xent->k, - len, xent->masklen, xent->type, xent->value) : + len, xent->masklen, xent->type, xent->mac_addr, xent->value) : ipfw_del_table_entry(chain, xent->tbl, &xent->k, len, xent->masklen, xent->type); } @@ -1245,6 +1476,54 @@ ipfw_ctl(struct sockopt *sopt) } break; + case IP_FW_TABLE_XZEROENTRY: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + error = ipfw_zero_table_xentry_stats(chain, xent); + if (!error) { + xent->timestamp += boottime.tv_sec; + error = sooptcopyout(sopt, xent, sizeof(*xent)); + } + } + break; + + case IP_FW_TABLE_XLISTENTRY: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + error = ipfw_lookup_table_xentry(chain, xent); + if (!error) { + xent->timestamp += boottime.tv_sec; + error = sooptcopyout(sopt, xent, sizeof(*xent)); + } + } + break; + case IP_FW_TABLE_XLIST: /* IP_FW3 */ { ipfw_xtable *tbl; @@ -1284,7 +1563,7 @@ ipfw_ctl(struct sockopt *sopt) /*--- NAT operations are protected by the IPFW_LOCK ---*/ case IP_FW_NAT_CFG: if (IPFW_NAT_LOADED) - error = ipfw_nat_cfg_ptr(sopt); + error = ipfw_nat_cfg_ptr(sopt, chain); else { printf("IP_FW_NAT_CFG: %s\n", "ipfw_nat not present, please load it"); @@ -1294,7 +1573,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_NAT_DEL: if (IPFW_NAT_LOADED) - error = ipfw_nat_del_ptr(sopt); + error = ipfw_nat_del_ptr(sopt, chain); else { printf("IP_FW_NAT_DEL: %s\n", "ipfw_nat not present, please load it"); @@ -1304,7 +1583,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_NAT_GET_CONFIG: if (IPFW_NAT_LOADED) - error = ipfw_nat_get_cfg_ptr(sopt); + error = ipfw_nat_get_cfg_ptr(sopt, chain); else { printf("IP_FW_NAT_GET_CFG: %s\n", "ipfw_nat not present, please load it"); @@ -1314,7 +1593,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_NAT_GET_LOG: if (IPFW_NAT_LOADED) - error = ipfw_nat_get_log_ptr(sopt); + error = ipfw_nat_get_log_ptr(sopt, chain); else { printf("IP_FW_NAT_GET_LOG: %s\n", "ipfw_nat not present, please load it"); @@ -1331,6 +1610,33 @@ ipfw_ctl(struct sockopt *sopt) #undef RULE_MAXSIZE } +void +ipfw_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + struct ip_fw_ctx_iflist *tmpifl; + + CURVNET_SET(ifp->if_vnet); + + IPFW_CTX_RLOCK(); + for (int i = 1; i < IP_FW_MAXCTX; i++) { + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + if (strlen(tmpifl->ifname) != strlen(ifp->if_xname)) + continue; + if (!strcmp(tmpifl->ifname, ifp->if_xname)) { + printf("Restoring context for interface %s to %d\n", ifp->if_xname, i); + ifp->if_ispare[0] = i; + goto ifctxdone; + break; + } + } + } +ifctxdone: + IPFW_CTX_RUNLOCK(); + + CURVNET_RESTORE(); +} #define RULE_MAXSIZE (256*sizeof(u_int32_t)) |