root/net/ipv6/netfilter/ip6t_srh.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. srh_mt6
  2. srh1_mt6
  3. srh_mt6_check
  4. srh1_mt6_check
  5. srh_mt6_init
  6. srh_mt6_exit

   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 
  24 static 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 
 116 static 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 
 246 static 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 
 265 static 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 
 284 static 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 
 305 static int __init srh_mt6_init(void)
 306 {
 307         return xt_register_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
 308 }
 309 
 310 static void __exit srh_mt6_exit(void)
 311 {
 312         xt_unregister_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
 313 }
 314 
 315 module_init(srh_mt6_init);
 316 module_exit(srh_mt6_exit);
 317 
 318 MODULE_LICENSE("GPL");
 319 MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
 320 MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");

/* [<][>][^][v][top][bottom][index][help] */