ip6t_srh.c (9072B)
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>");