Author: ae
Date: Sun Mar  5 23:48:24 2017
New Revision: 314716
URL: https://svnweb.freebsd.org/changeset/base/314716

Log:
  Add IPv6 support to O_IP_DST_LOOKUP opcode.
  
  o check the size of O_IP_SRC_LOOKUP opcode, it can not exceed the size of
    ipfw_insn_u32;
  o rename ipfw_lookup_table_extended() function into ipfw_lookup_table() and
    remove old ipfw_lookup_table();
  o use args->f_id.flow_id6 that is in host byte order to get DSCP value;
  o add SCTP ports support to 'lookup src/dst-port' opcode;
  o add IPv6 support to 'lookup src/dst-ip' opcode.
  
  PR:           217292
  Reviewed by:  melifaro
  MFC after:    2 weeks
  Sponsored by: Yandex LLC
  Differential Revision:        https://reviews.freebsd.org/D9873

Modified:
  head/sys/netpfil/ipfw/ip_fw2.c
  head/sys/netpfil/ipfw/ip_fw_private.h
  head/sys/netpfil/ipfw/ip_fw_sockopt.c
  head/sys/netpfil/ipfw/ip_fw_table.c

Modified: head/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw2.c      Sun Mar  5 22:19:43 2017        
(r314715)
+++ head/sys/netpfil/ipfw/ip_fw2.c      Sun Mar  5 23:48:24 2017        
(r314716)
@@ -382,8 +382,8 @@ iface_match(struct ifnet *ifp, ipfw_insn
        /* Check by name or by IP address */
        if (cmd->name[0] != '\0') { /* match by name */
                if (cmd->name[0] == '\1') /* use tablearg to match */
-                       return ipfw_lookup_table_extended(chain, cmd->p.kidx, 0,
-                               &ifp->if_index, tablearg);
+                       return ipfw_lookup_table(chain, cmd->p.kidx, 0,
+                           &ifp->if_index, tablearg);
                /* Check name */
                if (cmd->p.glob) {
                        if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
@@ -1455,86 +1455,130 @@ do {                                                   
        \
                                    src_ip.s_addr);
                                break;
 
-                       case O_IP_SRC_LOOKUP:
                        case O_IP_DST_LOOKUP:
-                               if (is_ipv4) {
-                                   uint32_t key =
-                                       (cmd->opcode == O_IP_DST_LOOKUP) ?
-                                           dst_ip.s_addr : src_ip.s_addr;
-                                   uint32_t v = 0;
-
-                                   if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) {
-                                       /* generic lookup. The key must be
-                                        * in 32bit big-endian format.
-                                        */
-                                       v = ((ipfw_insn_u32 *)cmd)->d[1];
-                                       if (v == 0)
-                                           key = dst_ip.s_addr;
-                                       else if (v == 1)
-                                           key = src_ip.s_addr;
-                                       else if (v == 6) /* dscp */
-                                           key = (ip->ip_tos >> 2) & 0x3f;
-                                       else if (offset != 0)
-                                           break;
-                                       else if (proto != IPPROTO_TCP &&
-                                               proto != IPPROTO_UDP)
-                                           break;
-                                       else if (v == 2)
-                                           key = dst_port;
-                                       else if (v == 3)
-                                           key = src_port;
+                       {
+                               void *pkey;
+                               uint32_t vidx, key;
+                               uint16_t keylen;
+
+                               if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) {
+                                       /* Determine lookup key type */
+                                       vidx = ((ipfw_insn_u32 *)cmd)->d[1];
+                                       if (vidx != 4 /* uid */ &&
+                                           vidx != 5 /* jail */ &&
+                                           is_ipv6 == 0 && is_ipv4 == 0)
+                                               break;
+                                       /* Determine key length */
+                                       if (vidx == 0 /* dst-ip */ ||
+                                           vidx == 1 /* src-ip */)
+                                               keylen = is_ipv6 ?
+                                                   sizeof(struct in6_addr):
+                                                   sizeof(in_addr_t);
+                                       else {
+                                               keylen = sizeof(key);
+                                               pkey = &key;
+                                       }
+                                       if (vidx == 0 /* dst-ip */)
+                                               pkey = is_ipv4 ? (void 
*)&dst_ip:
+                                                   (void *)&args->f_id.dst_ip6;
+                                       else if (vidx == 1 /* src-ip */)
+                                               pkey = is_ipv4 ? (void 
*)&src_ip:
+                                                   (void *)&args->f_id.src_ip6;
+                                       else if (vidx == 6 /* dscp */) {
+                                               if (is_ipv4)
+                                                       key = ip->ip_tos >> 2;
+                                               else {
+                                                       key = 
args->f_id.flow_id6;
+                                                       key = (key & 0x0f) << 2 
|
+                                                           (key & 0xf000) >> 
14;
+                                               }
+                                               key &= 0x3f;
+                                       } else if (vidx == 2 /* dst-port */ ||
+                                           vidx == 3 /* src-port */) {
+                                               /* Skip fragments */
+                                               if (offset != 0)
+                                                       break;
+                                               /* Skip proto without ports */
+                                               if (proto != IPPROTO_TCP &&
+                                                   proto != IPPROTO_UDP &&
+                                                   proto != IPPROTO_SCTP)
+                                                       break;
+                                               if (vidx == 2 /* dst-port */)
+                                                       key = dst_port;
+                                               else
+                                                       key = src_port;
+                                       }
 #ifndef USERSPACE
-                                       else if (v == 4 || v == 5) {
-                                           check_uidgid(
-                                               (ipfw_insn_u32 *)cmd,
-                                               args, &ucred_lookup,
+                                       else if (vidx == 4 /* uid */ ||
+                                           vidx == 5 /* jail */) {
+                                               check_uidgid(
+                                                   (ipfw_insn_u32 *)cmd,
+                                                   args, &ucred_lookup,
 #ifdef __FreeBSD__
-                                               &ucred_cache);
-                                           if (v == 4 /* O_UID */)
-                                               key = ucred_cache->cr_uid;
-                                           else if (v == 5 /* O_JAIL */)
-                                               key = 
ucred_cache->cr_prison->pr_id;
+                                                   &ucred_cache);
+                                               if (vidx == 4 /* uid */)
+                                                       key = 
ucred_cache->cr_uid;
+                                               else if (vidx == 5 /* jail */)
+                                                       key = 
ucred_cache->cr_prison->pr_id;
 #else /* !__FreeBSD__ */
-                                               (void *)&ucred_cache);
-                                           if (v ==4 /* O_UID */)
-                                               key = ucred_cache.uid;
-                                           else if (v == 5 /* O_JAIL */)
-                                               key = ucred_cache.xid;
+                                                   (void *)&ucred_cache);
+                                               if (vidx == 4 /* uid */)
+                                                       key = ucred_cache.uid;
+                                               else if (vidx == 5 /* jail */)
+                                                       key = ucred_cache.xid;
 #endif /* !__FreeBSD__ */
                                        }
 #endif /* !USERSPACE */
                                        else
-                                           break;
-                                   }
-                                   match = ipfw_lookup_table(chain,
-                                       cmd->arg1, key, &v);
-                                   if (!match)
+                                               break;
+                                       match = ipfw_lookup_table(chain,
+                                           cmd->arg1, keylen, pkey, &vidx);
+                                       if (!match)
+                                               break;
+                                       tablearg = vidx;
                                        break;
-                                   if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
-                                       match = ((ipfw_insn_u32 *)cmd)->d[0] ==
-                                           TARG_VAL(chain, v, tag);
-                                   else
-                                       tablearg = v;
+                               }
+                               /* cmdlen =< F_INSN_SIZE(ipfw_insn_u32) */
+                               /* FALLTHROUGH */
+                       }
+                       case O_IP_SRC_LOOKUP:
+                       {
+                               void *pkey;
+                               uint32_t vidx;
+                               uint16_t keylen;
+
+                               if (is_ipv4) {
+                                       keylen = sizeof(in_addr_t);
+                                       if (cmd->opcode == O_IP_DST_LOOKUP)
+                                               pkey = &dst_ip;
+                                       else
+                                               pkey = &src_ip;
                                } else if (is_ipv6) {
-                                       uint32_t v = 0;
-                                       void *pkey = (cmd->opcode == 
O_IP_DST_LOOKUP) ?
-                                               &args->f_id.dst_ip6: 
&args->f_id.src_ip6;
-                                       match = 
ipfw_lookup_table_extended(chain,
-                                                       cmd->arg1,
-                                                       sizeof(struct in6_addr),
-                                                       pkey, &v);
-                                       if (cmdlen == 
F_INSN_SIZE(ipfw_insn_u32))
-                                               match = ((ipfw_insn_u32 
*)cmd)->d[0] ==
-                                                   TARG_VAL(chain, v, tag);
-                                       if (match)
-                                               tablearg = v;
+                                       keylen = sizeof(struct in6_addr);
+                                       if (cmd->opcode == O_IP_DST_LOOKUP)
+                                               pkey = &args->f_id.dst_ip6;
+                                       else
+                                               pkey = &args->f_id.src_ip6;
+                               } else
+                                       break;
+                               match = ipfw_lookup_table(chain, cmd->arg1,
+                                   keylen, pkey, &vidx);
+                               if (!match)
+                                       break;
+                               if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) {
+                                       match = ((ipfw_insn_u32 *)cmd)->d[0] ==
+                                           TARG_VAL(chain, vidx, tag);
+                                       if (!match)
+                                               break;
                                }
+                               tablearg = vidx;
                                break;
+                       }
 
                        case O_IP_FLOW_LOOKUP:
                                {
                                        uint32_t v = 0;
-                                       match = 
ipfw_lookup_table_extended(chain,
+                                       match = ipfw_lookup_table(chain,
                                            cmd->arg1, 0, &args->f_id, &v);
                                        if (cmdlen == 
F_INSN_SIZE(ipfw_insn_u32))
                                                match = ((ipfw_insn_u32 
*)cmd)->d[0] ==

Modified: head/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_private.h       Sun Mar  5 22:19:43 2017        
(r314715)
+++ head/sys/netpfil/ipfw/ip_fw_private.h       Sun Mar  5 23:48:24 2017        
(r314716)
@@ -741,10 +741,8 @@ struct table_info;
 typedef int (table_lookup_t)(struct table_info *ti, void *key, uint32_t keylen,
     uint32_t *val);
 
-int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
-    uint32_t *val);
-int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl,
-    uint16_t plen, void *paddr, uint32_t *val);
+int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
+    void *paddr, uint32_t *val);
 struct named_object *ipfw_objhash_lookup_table_kidx(struct ip_fw_chain *ch,
     uint16_t kidx);
 int ipfw_ref_table(struct ip_fw_chain *ch, ipfw_obj_ntlv *ntlv, uint16_t 
*kidx);

Modified: head/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_sockopt.c       Sun Mar  5 22:19:43 2017        
(r314715)
+++ head/sys/netpfil/ipfw/ip_fw_sockopt.c       Sun Mar  5 23:48:24 2017        
(r314716)
@@ -1826,6 +1826,8 @@ check_ipfw_rule_body(ipfw_insn *cmd, int
                        break;
 
                case O_IP_SRC_LOOKUP:
+                       if (cmdlen > F_INSN_SIZE(ipfw_insn_u32))
+                               goto bad_size;
                case O_IP_DST_LOOKUP:
                        if (cmd->arg1 >= V_fw_tables_max) {
                                printf("ipfw: invalid table number %d\n",

Modified: head/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_table.c Sun Mar  5 22:19:43 2017        
(r314715)
+++ head/sys/netpfil/ipfw/ip_fw_table.c Sun Mar  5 23:48:24 2017        
(r314716)
@@ -1657,30 +1657,13 @@ ipfw_unref_table(struct ip_fw_chain *ch,
 }
 
 /*
- * Lookup an IP @addr in table @tbl.
- * Stores found value in @val.
- *
- * Returns 1 if @addr was found.
- */
-int
-ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
-    uint32_t *val)
-{
-       struct table_info *ti;
-
-       ti = KIDX_TO_TI(ch, tbl);
-
-       return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
-}
-
-/*
  * Lookup an arbtrary key @paddr of legth @plen in table @tbl.
  * Stores found value in @val.
  *
  * Returns 1 if key was found.
  */
 int
-ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
+ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
     void *paddr, uint32_t *val)
 {
        struct table_info *ti;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to