diff --git a/sys/net/dummymbuf.c b/sys/net/dummymbuf.c --- a/sys/net/dummymbuf.c +++ b/sys/net/dummymbuf.c @@ -40,6 +40,30 @@ #include #include +static int validate_rules(const char *); + +/* + * Logging + */ + +#define FEEDBACK_RULE(rule, msg) \ + printf("dummymbuf: %s: %.*s\n", \ + (msg), \ + (rule).syntax_len, (rule).syntax_begin \ + ) + +#define FEEDBACK_PFIL(pfil_type, pfil_flags, ifp, rule, msg) \ + printf("dummymbuf: %s %b %s: %s: %.*s\n", \ + (pfil_type == PFIL_TYPE_IP4 ? "PFIL_TYPE_IP4" : \ + pfil_type == PFIL_TYPE_IP6 ? "PFIL_TYPE_IP6" : \ + pfil_type == PFIL_TYPE_ETHERNET ? "PFIL_TYPE_ETHERNET" : \ + "PFIL_TYPE_UNKNOWN"), \ + (pfil_flags), "\20\21PFIL_IN\22PFIL_OUT", \ + (ifp)->if_xname, \ + (msg), \ + (rule).syntax_len, (rule).syntax_begin \ + ) + /* * Separate sysctl sub-tree */ @@ -65,6 +89,7 @@ #define DMB_RULES_SUNLOCK() sx_sunlock(&V_dmb_rules_lock) #define DMB_RULES_XLOCK() sx_xlock(&V_dmb_rules_lock) #define DMB_RULES_XUNLOCK() sx_xunlock(&V_dmb_rules_lock) +#define DMB_RULES_LOCK_ASSERT() sx_assert(&V_dmb_rules_lock, SA_LOCKED) static int dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS) @@ -86,12 +111,14 @@ } else { /* read and write */ DMB_RULES_XLOCK(); - if (*rulesp == NULL) { - *rulesp = malloc(arg2, M_DUMMYMBUF_RULES, - M_WAITOK | M_ZERO); - } - arg1 = *rulesp; + arg1 = malloc(arg2, M_DUMMYMBUF_RULES, M_WAITOK | M_ZERO); error = sysctl_handle_string(oidp, arg1, arg2, req); + if (error == 0 && (error = validate_rules(arg1)) == 0) { + free(*rulesp, M_DUMMYMBUF_RULES); + *rulesp = arg1; + arg1 = NULL; + } + free(arg1, M_DUMMYMBUF_RULES); DMB_RULES_XUNLOCK(); } @@ -101,7 +128,7 @@ SYSCTL_PROC(_net_dummymbuf, OID_AUTO, rules, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(dmb_rules), RULES_MAXLEN, dmb_sysctl_handle_rules, "A", - "{inet | inet6 | ethernet} {in | out} []; ...;"); + "{inet|inet6|ethernet} {in|out} [ ]; ...;"); /* * Statistics @@ -131,22 +158,6 @@ VNET_DEFINE_STATIC(pfil_hook_t, dmb_pfil_ethernet_hook); #define V_dmb_pfil_ethernet_hook VNET(dmb_pfil_ethernet_hook) -/* - * Logging - */ - -#define FEEDBACK(pfil_type, pfil_flags, ifp, rule, msg) \ - printf("dummymbuf: %s %b %s: %s: %.*s\n", \ - (pfil_type == PFIL_TYPE_IP4 ? "PFIL_TYPE_IP4" : \ - pfil_type == PFIL_TYPE_IP6 ? "PFIL_TYPE_IP6" : \ - pfil_type == PFIL_TYPE_ETHERNET ? "PFIL_TYPE_ETHERNET" : \ - "PFIL_TYPE_UNKNOWN"), \ - (pfil_flags), "\20\21PFIL_IN\22PFIL_OUT", \ - (ifp)->if_xname, \ - (msg), \ - (rule).syntax_len, (rule).syntax_begin \ - ) - /* * Internals */ @@ -198,9 +209,9 @@ } static bool -read_rule(const char **cur, struct rule *rule) +read_rule(const char **cur, struct rule *rule, bool *eof) { - // {inet | inet6 | ethernet} {in | out} [ ]; + /* {inet|inet6|ethernet} {in|out} [ ]; */ rule->syntax_begin = NULL; rule->syntax_len = 0; @@ -208,18 +219,19 @@ if (*cur == NULL) return (false); - // syntax_begin + /* syntax_begin */ while (**cur == ' ') (*cur)++; rule->syntax_begin = *cur; + rule->syntax_len = strlen(rule->syntax_begin); - // syntax_len + /* syntax_len */ char *delim = strchr(*cur, ';'); if (delim == NULL) return (false); rule->syntax_len = (int)(delim - *cur + 1); - // pfil_type + /* pfil_type */ if (strstr(*cur, "inet6") == *cur) { rule->pfil_type = PFIL_TYPE_IP6; *cur += strlen("inet6"); @@ -235,7 +247,7 @@ while (**cur == ' ') (*cur)++; - // pfil_dir + /* pfil_dir */ if (strstr(*cur, "in") == *cur) { rule->pfil_dir = PFIL_IN; *cur += strlen("in"); @@ -248,7 +260,7 @@ while (**cur == ' ') (*cur)++; - // ifname + /* ifname */ char *sp = strchr(*cur, ' '); if (sp == NULL || sp > delim) return (false); @@ -261,7 +273,7 @@ while (**cur == ' ') (*cur)++; - // opname + /* opname */ if (strstr(*cur, "pull-head") == *cur) { rule->op = dmb_m_pull_head; *cur += strlen("pull-head"); @@ -271,16 +283,42 @@ while (**cur == ' ') (*cur)++; - // opargs + /* opargs */ if (*cur > delim) return (false); rule->opargs = *cur; + /* the next rule & eof */ *cur = delim + 1; + while (**cur == ' ') + (*cur)++; + *eof = strlen(*cur) == 0; return (true); } +static int +validate_rules(const char *rules) +{ + const char *cursor = rules; + bool parsed; + struct rule rule; + bool eof = false; + + DMB_RULES_LOCK_ASSERT(); + + while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) { + /* noop */ + } + + if (!parsed) { + FEEDBACK_RULE(rule, "rule parsing failed"); + return (EINVAL); + } + + return (0); +} + static pfil_return_t dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp, int flags, void *ruleset, void *unused) @@ -289,26 +327,26 @@ const char *cursor; bool parsed; struct rule rule; + bool eof = false; DMB_RULES_SLOCK(); cursor = V_dmb_rules; - while ((parsed = read_rule(&cursor, &rule))) { + while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) { if (rule.pfil_type == pfil_type && rule.pfil_dir == (flags & rule.pfil_dir) && strcmp(rule.ifname, ifp->if_xname) == 0) { m = rule.op(m, &rule); if (m == NULL) { - FEEDBACK(pfil_type, flags, ifp, rule, + FEEDBACK_PFIL(pfil_type, flags, ifp, rule, "mbuf operation failed"); break; } counter_u64_add(V_dmb_hits, 1); } - if (strlen(cursor) == 0) - break; } if (!parsed) { - FEEDBACK(pfil_type, flags, ifp, rule, "rule parsing failed"); + FEEDBACK_PFIL(pfil_type, flags, ifp, rule, + "rule parsing failed"); m_freem(m); m = NULL; }