net/sched: act_pedit: Add size check for TCA_PEDIT_PARMS_EX
[platform/kernel/linux-starfive.git] / net / sched / act_pedit.c
index 19f6b3f..aee2e13 100644 (file)
 #include <linux/rtnetlink.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/slab.h>
+#include <net/ipv6.h>
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
 #include <linux/tc_act/tc_pedit.h>
@@ -25,6 +28,7 @@ static struct tc_action_ops act_pedit_ops;
 
 static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
        [TCA_PEDIT_PARMS]       = { .len = sizeof(struct tc_pedit) },
+       [TCA_PEDIT_PARMS_EX]    = { .len = sizeof(struct tc_pedit) },
        [TCA_PEDIT_KEYS_EX]   = { .type = NLA_NESTED },
 };
 
@@ -312,11 +316,35 @@ static bool offset_valid(struct sk_buff *skb, int offset)
        return true;
 }
 
-static int pedit_skb_hdr_offset(struct sk_buff *skb,
-                               enum pedit_header_type htype, int *hoffset)
+static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
 {
+       const int noff = skb_network_offset(skb);
        int ret = -EINVAL;
+       struct iphdr _iph;
+
+       switch (skb->protocol) {
+       case htons(ETH_P_IP): {
+               const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph);
+
+               if (!iph)
+                       goto out;
+               *hoffset = noff + iph->ihl * 4;
+               ret = 0;
+               break;
+       }
+       case htons(ETH_P_IPV6):
+               ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL;
+               break;
+       }
+out:
+       return ret;
+}
 
+static int pedit_skb_hdr_offset(struct sk_buff *skb,
+                                enum pedit_header_type htype, int *hoffset)
+{
+       int ret = -EINVAL;
+       /* 'htype' is validated in the netlink parsing */
        switch (htype) {
        case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
                if (skb_mac_header_was_set(skb)) {
@@ -331,17 +359,14 @@ static int pedit_skb_hdr_offset(struct sk_buff *skb,
                ret = 0;
                break;
        case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
+               ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP);
+               break;
        case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
-               if (skb_transport_header_was_set(skb)) {
-                       *hoffset = skb_transport_offset(skb);
-                       ret = 0;
-               }
+               ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP);
                break;
        default:
-               ret = -EINVAL;
                break;
        }
-
        return ret;
 }
 
@@ -374,8 +399,8 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
 
        for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
                int offset = tkey->off;
+               int hoffset = 0;
                u32 *ptr, hdata;
-               int hoffset;
                u32 val;
                int rc;
 
@@ -388,8 +413,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
 
                rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
                if (rc) {
-                       pr_info("tc action pedit bad header type specified (0x%x)\n",
-                               htype);
+                       pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype);
                        goto bad;
                }