Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /* net/sched/act_ctinfo.c  netfilter ctinfo connmark actions
0003  *
0004  * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
0005  */
0006 
0007 #include <linux/module.h>
0008 #include <linux/init.h>
0009 #include <linux/kernel.h>
0010 #include <linux/skbuff.h>
0011 #include <linux/rtnetlink.h>
0012 #include <linux/pkt_cls.h>
0013 #include <linux/ip.h>
0014 #include <linux/ipv6.h>
0015 #include <net/netlink.h>
0016 #include <net/pkt_sched.h>
0017 #include <net/act_api.h>
0018 #include <net/pkt_cls.h>
0019 #include <uapi/linux/tc_act/tc_ctinfo.h>
0020 #include <net/tc_act/tc_ctinfo.h>
0021 
0022 #include <net/netfilter/nf_conntrack.h>
0023 #include <net/netfilter/nf_conntrack_core.h>
0024 #include <net/netfilter/nf_conntrack_ecache.h>
0025 #include <net/netfilter/nf_conntrack_zones.h>
0026 
0027 static struct tc_action_ops act_ctinfo_ops;
0028 static unsigned int ctinfo_net_id;
0029 
0030 static void tcf_ctinfo_dscp_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
0031                 struct tcf_ctinfo_params *cp,
0032                 struct sk_buff *skb, int wlen, int proto)
0033 {
0034     u8 dscp, newdscp;
0035 
0036     newdscp = (((ct->mark & cp->dscpmask) >> cp->dscpmaskshift) << 2) &
0037              ~INET_ECN_MASK;
0038 
0039     switch (proto) {
0040     case NFPROTO_IPV4:
0041         dscp = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK;
0042         if (dscp != newdscp) {
0043             if (likely(!skb_try_make_writable(skb, wlen))) {
0044                 ipv4_change_dsfield(ip_hdr(skb),
0045                             INET_ECN_MASK,
0046                             newdscp);
0047                 ca->stats_dscp_set++;
0048             } else {
0049                 ca->stats_dscp_error++;
0050             }
0051         }
0052         break;
0053     case NFPROTO_IPV6:
0054         dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK;
0055         if (dscp != newdscp) {
0056             if (likely(!skb_try_make_writable(skb, wlen))) {
0057                 ipv6_change_dsfield(ipv6_hdr(skb),
0058                             INET_ECN_MASK,
0059                             newdscp);
0060                 ca->stats_dscp_set++;
0061             } else {
0062                 ca->stats_dscp_error++;
0063             }
0064         }
0065         break;
0066     default:
0067         break;
0068     }
0069 }
0070 
0071 static void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
0072                   struct tcf_ctinfo_params *cp,
0073                   struct sk_buff *skb)
0074 {
0075     ca->stats_cpmark_set++;
0076     skb->mark = ct->mark & cp->cpmarkmask;
0077 }
0078 
0079 static int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a,
0080               struct tcf_result *res)
0081 {
0082     const struct nf_conntrack_tuple_hash *thash = NULL;
0083     struct tcf_ctinfo *ca = to_ctinfo(a);
0084     struct nf_conntrack_tuple tuple;
0085     struct nf_conntrack_zone zone;
0086     enum ip_conntrack_info ctinfo;
0087     struct tcf_ctinfo_params *cp;
0088     struct nf_conn *ct;
0089     int proto, wlen;
0090     int action;
0091 
0092     cp = rcu_dereference_bh(ca->params);
0093 
0094     tcf_lastuse_update(&ca->tcf_tm);
0095     bstats_update(&ca->tcf_bstats, skb);
0096     action = READ_ONCE(ca->tcf_action);
0097 
0098     wlen = skb_network_offset(skb);
0099     switch (skb_protocol(skb, true)) {
0100     case htons(ETH_P_IP):
0101         wlen += sizeof(struct iphdr);
0102         if (!pskb_may_pull(skb, wlen))
0103             goto out;
0104 
0105         proto = NFPROTO_IPV4;
0106         break;
0107     case htons(ETH_P_IPV6):
0108         wlen += sizeof(struct ipv6hdr);
0109         if (!pskb_may_pull(skb, wlen))
0110             goto out;
0111 
0112         proto = NFPROTO_IPV6;
0113         break;
0114     default:
0115         goto out;
0116     }
0117 
0118     ct = nf_ct_get(skb, &ctinfo);
0119     if (!ct) { /* look harder, usually ingress */
0120         if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
0121                        proto, cp->net, &tuple))
0122             goto out;
0123         zone.id = cp->zone;
0124         zone.dir = NF_CT_DEFAULT_ZONE_DIR;
0125 
0126         thash = nf_conntrack_find_get(cp->net, &zone, &tuple);
0127         if (!thash)
0128             goto out;
0129 
0130         ct = nf_ct_tuplehash_to_ctrack(thash);
0131     }
0132 
0133     if (cp->mode & CTINFO_MODE_DSCP)
0134         if (!cp->dscpstatemask || (ct->mark & cp->dscpstatemask))
0135             tcf_ctinfo_dscp_set(ct, ca, cp, skb, wlen, proto);
0136 
0137     if (cp->mode & CTINFO_MODE_CPMARK)
0138         tcf_ctinfo_cpmark_set(ct, ca, cp, skb);
0139 
0140     if (thash)
0141         nf_ct_put(ct);
0142 out:
0143     return action;
0144 }
0145 
0146 static const struct nla_policy ctinfo_policy[TCA_CTINFO_MAX + 1] = {
0147     [TCA_CTINFO_ACT]          =
0148         NLA_POLICY_EXACT_LEN(sizeof(struct tc_ctinfo)),
0149     [TCA_CTINFO_ZONE]         = { .type = NLA_U16 },
0150     [TCA_CTINFO_PARMS_DSCP_MASK]      = { .type = NLA_U32 },
0151     [TCA_CTINFO_PARMS_DSCP_STATEMASK] = { .type = NLA_U32 },
0152     [TCA_CTINFO_PARMS_CPMARK_MASK]    = { .type = NLA_U32 },
0153 };
0154 
0155 static int tcf_ctinfo_init(struct net *net, struct nlattr *nla,
0156                struct nlattr *est, struct tc_action **a,
0157                struct tcf_proto *tp, u32 flags,
0158                struct netlink_ext_ack *extack)
0159 {
0160     struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
0161     bool bind = flags & TCA_ACT_FLAGS_BIND;
0162     u32 dscpmask = 0, dscpstatemask, index;
0163     struct nlattr *tb[TCA_CTINFO_MAX + 1];
0164     struct tcf_ctinfo_params *cp_new;
0165     struct tcf_chain *goto_ch = NULL;
0166     struct tc_ctinfo *actparm;
0167     struct tcf_ctinfo *ci;
0168     u8 dscpmaskshift;
0169     int ret = 0, err;
0170 
0171     if (!nla) {
0172         NL_SET_ERR_MSG_MOD(extack, "ctinfo requires attributes to be passed");
0173         return -EINVAL;
0174     }
0175 
0176     err = nla_parse_nested(tb, TCA_CTINFO_MAX, nla, ctinfo_policy, extack);
0177     if (err < 0)
0178         return err;
0179 
0180     if (!tb[TCA_CTINFO_ACT]) {
0181         NL_SET_ERR_MSG_MOD(extack,
0182                    "Missing required TCA_CTINFO_ACT attribute");
0183         return -EINVAL;
0184     }
0185     actparm = nla_data(tb[TCA_CTINFO_ACT]);
0186 
0187     /* do some basic validation here before dynamically allocating things */
0188     /* that we would otherwise have to clean up.                  */
0189     if (tb[TCA_CTINFO_PARMS_DSCP_MASK]) {
0190         dscpmask = nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_MASK]);
0191         /* need contiguous 6 bit mask */
0192         dscpmaskshift = dscpmask ? __ffs(dscpmask) : 0;
0193         if ((~0 & (dscpmask >> dscpmaskshift)) != 0x3f) {
0194             NL_SET_ERR_MSG_ATTR(extack,
0195                         tb[TCA_CTINFO_PARMS_DSCP_MASK],
0196                         "dscp mask must be 6 contiguous bits");
0197             return -EINVAL;
0198         }
0199         dscpstatemask = tb[TCA_CTINFO_PARMS_DSCP_STATEMASK] ?
0200             nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_STATEMASK]) : 0;
0201         /* mask & statemask must not overlap */
0202         if (dscpmask & dscpstatemask) {
0203             NL_SET_ERR_MSG_ATTR(extack,
0204                         tb[TCA_CTINFO_PARMS_DSCP_STATEMASK],
0205                         "dscp statemask must not overlap dscp mask");
0206             return -EINVAL;
0207         }
0208     }
0209 
0210     /* done the validation:now to the actual action allocation */
0211     index = actparm->index;
0212     err = tcf_idr_check_alloc(tn, &index, a, bind);
0213     if (!err) {
0214         ret = tcf_idr_create(tn, index, est, a,
0215                      &act_ctinfo_ops, bind, false, flags);
0216         if (ret) {
0217             tcf_idr_cleanup(tn, index);
0218             return ret;
0219         }
0220         ret = ACT_P_CREATED;
0221     } else if (err > 0) {
0222         if (bind) /* don't override defaults */
0223             return 0;
0224         if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
0225             tcf_idr_release(*a, bind);
0226             return -EEXIST;
0227         }
0228     } else {
0229         return err;
0230     }
0231 
0232     err = tcf_action_check_ctrlact(actparm->action, tp, &goto_ch, extack);
0233     if (err < 0)
0234         goto release_idr;
0235 
0236     ci = to_ctinfo(*a);
0237 
0238     cp_new = kzalloc(sizeof(*cp_new), GFP_KERNEL);
0239     if (unlikely(!cp_new)) {
0240         err = -ENOMEM;
0241         goto put_chain;
0242     }
0243 
0244     cp_new->net = net;
0245     cp_new->zone = tb[TCA_CTINFO_ZONE] ?
0246             nla_get_u16(tb[TCA_CTINFO_ZONE]) : 0;
0247     if (dscpmask) {
0248         cp_new->dscpmask = dscpmask;
0249         cp_new->dscpmaskshift = dscpmaskshift;
0250         cp_new->dscpstatemask = dscpstatemask;
0251         cp_new->mode |= CTINFO_MODE_DSCP;
0252     }
0253 
0254     if (tb[TCA_CTINFO_PARMS_CPMARK_MASK]) {
0255         cp_new->cpmarkmask =
0256                 nla_get_u32(tb[TCA_CTINFO_PARMS_CPMARK_MASK]);
0257         cp_new->mode |= CTINFO_MODE_CPMARK;
0258     }
0259 
0260     spin_lock_bh(&ci->tcf_lock);
0261     goto_ch = tcf_action_set_ctrlact(*a, actparm->action, goto_ch);
0262     cp_new = rcu_replace_pointer(ci->params, cp_new,
0263                      lockdep_is_held(&ci->tcf_lock));
0264     spin_unlock_bh(&ci->tcf_lock);
0265 
0266     if (goto_ch)
0267         tcf_chain_put_by_act(goto_ch);
0268     if (cp_new)
0269         kfree_rcu(cp_new, rcu);
0270 
0271     return ret;
0272 
0273 put_chain:
0274     if (goto_ch)
0275         tcf_chain_put_by_act(goto_ch);
0276 release_idr:
0277     tcf_idr_release(*a, bind);
0278     return err;
0279 }
0280 
0281 static int tcf_ctinfo_dump(struct sk_buff *skb, struct tc_action *a,
0282                int bind, int ref)
0283 {
0284     struct tcf_ctinfo *ci = to_ctinfo(a);
0285     struct tc_ctinfo opt = {
0286         .index   = ci->tcf_index,
0287         .refcnt  = refcount_read(&ci->tcf_refcnt) - ref,
0288         .bindcnt = atomic_read(&ci->tcf_bindcnt) - bind,
0289     };
0290     unsigned char *b = skb_tail_pointer(skb);
0291     struct tcf_ctinfo_params *cp;
0292     struct tcf_t t;
0293 
0294     spin_lock_bh(&ci->tcf_lock);
0295     cp = rcu_dereference_protected(ci->params,
0296                        lockdep_is_held(&ci->tcf_lock));
0297 
0298     tcf_tm_dump(&t, &ci->tcf_tm);
0299     if (nla_put_64bit(skb, TCA_CTINFO_TM, sizeof(t), &t, TCA_CTINFO_PAD))
0300         goto nla_put_failure;
0301 
0302     opt.action = ci->tcf_action;
0303     if (nla_put(skb, TCA_CTINFO_ACT, sizeof(opt), &opt))
0304         goto nla_put_failure;
0305 
0306     if (nla_put_u16(skb, TCA_CTINFO_ZONE, cp->zone))
0307         goto nla_put_failure;
0308 
0309     if (cp->mode & CTINFO_MODE_DSCP) {
0310         if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_MASK,
0311                 cp->dscpmask))
0312             goto nla_put_failure;
0313         if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_STATEMASK,
0314                 cp->dscpstatemask))
0315             goto nla_put_failure;
0316     }
0317 
0318     if (cp->mode & CTINFO_MODE_CPMARK) {
0319         if (nla_put_u32(skb, TCA_CTINFO_PARMS_CPMARK_MASK,
0320                 cp->cpmarkmask))
0321             goto nla_put_failure;
0322     }
0323 
0324     if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_SET,
0325                   ci->stats_dscp_set, TCA_CTINFO_PAD))
0326         goto nla_put_failure;
0327 
0328     if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_ERROR,
0329                   ci->stats_dscp_error, TCA_CTINFO_PAD))
0330         goto nla_put_failure;
0331 
0332     if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_CPMARK_SET,
0333                   ci->stats_cpmark_set, TCA_CTINFO_PAD))
0334         goto nla_put_failure;
0335 
0336     spin_unlock_bh(&ci->tcf_lock);
0337     return skb->len;
0338 
0339 nla_put_failure:
0340     spin_unlock_bh(&ci->tcf_lock);
0341     nlmsg_trim(skb, b);
0342     return -1;
0343 }
0344 
0345 static int tcf_ctinfo_walker(struct net *net, struct sk_buff *skb,
0346                  struct netlink_callback *cb, int type,
0347                  const struct tc_action_ops *ops,
0348                  struct netlink_ext_ack *extack)
0349 {
0350     struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
0351 
0352     return tcf_generic_walker(tn, skb, cb, type, ops, extack);
0353 }
0354 
0355 static int tcf_ctinfo_search(struct net *net, struct tc_action **a, u32 index)
0356 {
0357     struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
0358 
0359     return tcf_idr_search(tn, a, index);
0360 }
0361 
0362 static void tcf_ctinfo_cleanup(struct tc_action *a)
0363 {
0364     struct tcf_ctinfo *ci = to_ctinfo(a);
0365     struct tcf_ctinfo_params *cp;
0366 
0367     cp = rcu_dereference_protected(ci->params, 1);
0368     if (cp)
0369         kfree_rcu(cp, rcu);
0370 }
0371 
0372 static struct tc_action_ops act_ctinfo_ops = {
0373     .kind   = "ctinfo",
0374     .id = TCA_ID_CTINFO,
0375     .owner  = THIS_MODULE,
0376     .act    = tcf_ctinfo_act,
0377     .dump   = tcf_ctinfo_dump,
0378     .init   = tcf_ctinfo_init,
0379     .cleanup= tcf_ctinfo_cleanup,
0380     .walk   = tcf_ctinfo_walker,
0381     .lookup = tcf_ctinfo_search,
0382     .size   = sizeof(struct tcf_ctinfo),
0383 };
0384 
0385 static __net_init int ctinfo_init_net(struct net *net)
0386 {
0387     struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
0388 
0389     return tc_action_net_init(net, tn, &act_ctinfo_ops);
0390 }
0391 
0392 static void __net_exit ctinfo_exit_net(struct list_head *net_list)
0393 {
0394     tc_action_net_exit(net_list, ctinfo_net_id);
0395 }
0396 
0397 static struct pernet_operations ctinfo_net_ops = {
0398     .init       = ctinfo_init_net,
0399     .exit_batch = ctinfo_exit_net,
0400     .id     = &ctinfo_net_id,
0401     .size       = sizeof(struct tc_action_net),
0402 };
0403 
0404 static int __init ctinfo_init_module(void)
0405 {
0406     return tcf_register_action(&act_ctinfo_ops, &ctinfo_net_ops);
0407 }
0408 
0409 static void __exit ctinfo_cleanup_module(void)
0410 {
0411     tcf_unregister_action(&act_ctinfo_ops, &ctinfo_net_ops);
0412 }
0413 
0414 module_init(ctinfo_init_module);
0415 module_exit(ctinfo_cleanup_module);
0416 MODULE_AUTHOR("Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>");
0417 MODULE_DESCRIPTION("Connection tracking mark actions");
0418 MODULE_LICENSE("GPL");