summaryrefslogtreecommitdiffstats
path: root/sys/netpfil/ipfw/ip_fw_sockopt.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netpfil/ipfw/ip_fw_sockopt.c')
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c330
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))
OpenPOWER on IntegriCloud