linux/net/ipv6/netfilter/ip6t_srh.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* Kernel module to match Segment Routing Header (SRH) parameters. */
   3
   4/* Author:
   5 * Ahmed Abdelsalam <amsalam20@gmail.com>
   6 */
   7
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9#include <linux/module.h>
  10#include <linux/skbuff.h>
  11#include <linux/ipv6.h>
  12#include <linux/types.h>
  13#include <net/ipv6.h>
  14#include <net/seg6.h>
  15
  16#include <linux/netfilter/x_tables.h>
  17#include <linux/netfilter_ipv6/ip6t_srh.h>
  18#include <linux/netfilter_ipv6/ip6_tables.h>
  19
  20/* Test a struct->mt_invflags and a boolean for inequality */
  21#define NF_SRH_INVF(ptr, flag, boolean) \
  22        ((boolean) ^ !!((ptr)->mt_invflags & (flag)))
  23
  24static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
  25{
  26        const struct ip6t_srh *srhinfo = par->matchinfo;
  27        struct ipv6_sr_hdr *srh;
  28        struct ipv6_sr_hdr _srh;
  29        int hdrlen, srhoff = 0;
  30
  31        if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
  32                return false;
  33        srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
  34        if (!srh)
  35                return false;
  36
  37        hdrlen = ipv6_optlen(srh);
  38        if (skb->len - srhoff < hdrlen)
  39                return false;
  40
  41        if (srh->type != IPV6_SRCRT_TYPE_4)
  42                return false;
  43
  44        if (srh->segments_left > srh->first_segment)
  45                return false;
  46
  47        /* Next Header matching */
  48        if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
  49                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
  50                                !(srh->nexthdr == srhinfo->next_hdr)))
  51                        return false;
  52
  53        /* Header Extension Length matching */
  54        if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
  55                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
  56                                !(srh->hdrlen == srhinfo->hdr_len)))
  57                        return false;
  58
  59        if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
  60                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
  61                                !(srh->hdrlen > srhinfo->hdr_len)))
  62                        return false;
  63
  64        if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
  65                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
  66                                !(srh->hdrlen < srhinfo->hdr_len)))
  67                        return false;
  68
  69        /* Segments Left matching */
  70        if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
  71                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
  72                                !(srh->segments_left == srhinfo->segs_left)))
  73                        return false;
  74
  75        if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
  76                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
  77                                !(srh->segments_left > srhinfo->segs_left)))
  78                        return false;
  79
  80        if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
  81                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
  82                                !(srh->segments_left < srhinfo->segs_left)))
  83                        return false;
  84
  85        /**
  86         * Last Entry matching
  87         * Last_Entry field was introduced in revision 6 of the SRH draft.
  88         * It was called First_Segment in the previous revision
  89         */
  90        if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
  91                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
  92                                !(srh->first_segment == srhinfo->last_entry)))
  93                        return false;
  94
  95        if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
  96                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
  97                                !(srh->first_segment > srhinfo->last_entry)))
  98                        return false;
  99
 100        if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
 101                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
 102                                !(srh->first_segment < srhinfo->last_entry)))
 103                        return false;
 104
 105        /**
 106         * Tag matchig
 107         * Tag field was introduced in revision 6 of the SRH draft.
 108         */
 109        if (srhinfo->mt_flags & IP6T_SRH_TAG)
 110                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
 111                                !(srh->tag == srhinfo->tag)))
 112                        return false;
 113        return true;
 114}
 115
 116static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
 117{
 118        int hdrlen, psidoff, nsidoff, lsidoff, srhoff = 0;
 119        const struct ip6t_srh1 *srhinfo = par->matchinfo;
 120        struct in6_addr *psid, *nsid, *lsid;
 121        struct in6_addr _psid, _nsid, _lsid;
 122        struct ipv6_sr_hdr *srh;
 123        struct ipv6_sr_hdr _srh;
 124
 125        if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
 126                return false;
 127        srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
 128        if (!srh)
 129                return false;
 130
 131        hdrlen = ipv6_optlen(srh);
 132        if (skb->len - srhoff < hdrlen)
 133                return false;
 134
 135        if (srh->type != IPV6_SRCRT_TYPE_4)
 136                return false;
 137
 138        if (srh->segments_left > srh->first_segment)
 139                return false;
 140
 141        /* Next Header matching */
 142        if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
 143                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
 144                                !(srh->nexthdr == srhinfo->next_hdr)))
 145                        return false;
 146
 147        /* Header Extension Length matching */
 148        if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
 149                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
 150                                !(srh->hdrlen == srhinfo->hdr_len)))
 151                        return false;
 152        if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
 153                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
 154                                !(srh->hdrlen > srhinfo->hdr_len)))
 155                        return false;
 156        if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
 157                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
 158                                !(srh->hdrlen < srhinfo->hdr_len)))
 159                        return false;
 160
 161        /* Segments Left matching */
 162        if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
 163                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
 164                                !(srh->segments_left == srhinfo->segs_left)))
 165                        return false;
 166        if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
 167                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
 168                                !(srh->segments_left > srhinfo->segs_left)))
 169                        return false;
 170        if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
 171                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
 172                                !(srh->segments_left < srhinfo->segs_left)))
 173                        return false;
 174
 175        /**
 176         * Last Entry matching
 177         * Last_Entry field was introduced in revision 6 of the SRH draft.
 178         * It was called First_Segment in the previous revision
 179         */
 180        if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
 181                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
 182                                !(srh->first_segment == srhinfo->last_entry)))
 183                        return false;
 184        if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
 185                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
 186                                !(srh->first_segment > srhinfo->last_entry)))
 187                        return false;
 188        if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
 189                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
 190                                !(srh->first_segment < srhinfo->last_entry)))
 191                        return false;
 192
 193        /**
 194         * Tag matchig
 195         * Tag field was introduced in revision 6 of the SRH draft
 196         */
 197        if (srhinfo->mt_flags & IP6T_SRH_TAG)
 198                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
 199                                !(srh->tag == srhinfo->tag)))
 200                        return false;
 201
 202        /* Previous SID matching */
 203        if (srhinfo->mt_flags & IP6T_SRH_PSID) {
 204                if (srh->segments_left == srh->first_segment)
 205                        return false;
 206                psidoff = srhoff + sizeof(struct ipv6_sr_hdr) +
 207                          ((srh->segments_left + 1) * sizeof(struct in6_addr));
 208                psid = skb_header_pointer(skb, psidoff, sizeof(_psid), &_psid);
 209                if (!psid)
 210                        return false;
 211                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_PSID,
 212                                ipv6_masked_addr_cmp(psid, &srhinfo->psid_msk,
 213                                                     &srhinfo->psid_addr)))
 214                        return false;
 215        }
 216
 217        /* Next SID matching */
 218        if (srhinfo->mt_flags & IP6T_SRH_NSID) {
 219                if (srh->segments_left == 0)
 220                        return false;
 221                nsidoff = srhoff + sizeof(struct ipv6_sr_hdr) +
 222                          ((srh->segments_left - 1) * sizeof(struct in6_addr));
 223                nsid = skb_header_pointer(skb, nsidoff, sizeof(_nsid), &_nsid);
 224                if (!nsid)
 225                        return false;
 226                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NSID,
 227                                ipv6_masked_addr_cmp(nsid, &srhinfo->nsid_msk,
 228                                                     &srhinfo->nsid_addr)))
 229                        return false;
 230        }
 231
 232        /* Last SID matching */
 233        if (srhinfo->mt_flags & IP6T_SRH_LSID) {
 234                lsidoff = srhoff + sizeof(struct ipv6_sr_hdr);
 235                lsid = skb_header_pointer(skb, lsidoff, sizeof(_lsid), &_lsid);
 236                if (!lsid)
 237                        return false;
 238                if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LSID,
 239                                ipv6_masked_addr_cmp(lsid, &srhinfo->lsid_msk,
 240                                                     &srhinfo->lsid_addr)))
 241                        return false;
 242        }
 243        return true;
 244}
 245
 246static int srh_mt6_check(const struct xt_mtchk_param *par)
 247{
 248        const struct ip6t_srh *srhinfo = par->matchinfo;
 249
 250        if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
 251                pr_info_ratelimited("unknown srh match flags  %X\n",
 252                                    srhinfo->mt_flags);
 253                return -EINVAL;
 254        }
 255
 256        if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
 257                pr_info_ratelimited("unknown srh invflags %X\n",
 258                                    srhinfo->mt_invflags);
 259                return -EINVAL;
 260        }
 261
 262        return 0;
 263}
 264
 265static int srh1_mt6_check(const struct xt_mtchk_param *par)
 266{
 267        const struct ip6t_srh1 *srhinfo = par->matchinfo;
 268
 269        if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
 270                pr_info_ratelimited("unknown srh match flags  %X\n",
 271                                    srhinfo->mt_flags);
 272                return -EINVAL;
 273        }
 274
 275        if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
 276                pr_info_ratelimited("unknown srh invflags %X\n",
 277                                    srhinfo->mt_invflags);
 278                return -EINVAL;
 279        }
 280
 281        return 0;
 282}
 283
 284static struct xt_match srh_mt6_reg[] __read_mostly = {
 285        {
 286                .name           = "srh",
 287                .revision       = 0,
 288                .family         = NFPROTO_IPV6,
 289                .match          = srh_mt6,
 290                .matchsize      = sizeof(struct ip6t_srh),
 291                .checkentry     = srh_mt6_check,
 292                .me             = THIS_MODULE,
 293        },
 294        {
 295                .name           = "srh",
 296                .revision       = 1,
 297                .family         = NFPROTO_IPV6,
 298                .match          = srh1_mt6,
 299                .matchsize      = sizeof(struct ip6t_srh1),
 300                .checkentry     = srh1_mt6_check,
 301                .me             = THIS_MODULE,
 302        }
 303};
 304
 305static int __init srh_mt6_init(void)
 306{
 307        return xt_register_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
 308}
 309
 310static void __exit srh_mt6_exit(void)
 311{
 312        xt_unregister_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
 313}
 314
 315module_init(srh_mt6_init);
 316module_exit(srh_mt6_exit);
 317
 318MODULE_LICENSE("GPL");
 319MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
 320MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");
 321