Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: ip6t_srh: extend SRH matching for previous, next and last SID

IPv6 Segment Routing Header (SRH) contains a list of SIDs to be crossed
by SR encapsulated packet. Each SID is encoded as an IPv6 prefix.

When a Firewall receives an SR encapsulated packet, it should be able
to identify which node previously processed the packet (previous SID),
which node is going to process the packet next (next SID), and which
node is the last to process the packet (last SID) which represent the
final destination of the packet in case of inline SR mode.

An example use-case of using these features could be SID list that
includes two firewalls. When the second firewall receives a packet,
it can check whether the packet has been processed by the first firewall
or not. Based on that check, it decides to apply all rules, apply just
subset of the rules, or totally skip all rules and forward the packet to
the next SID.

This patch extends SRH match to support matching previous SID, next SID,
and last SID.

Signed-off-by: Ahmed Abdelsalam <amsalam20@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Ahmed Abdelsalam and committed by
Pablo Neira Ayuso
c1c7e44b 75e72f05

+205 -11
+41 -2
include/uapi/linux/netfilter_ipv6/ip6t_srh.h
··· 17 17 #define IP6T_SRH_LAST_GT 0x0100 18 18 #define IP6T_SRH_LAST_LT 0x0200 19 19 #define IP6T_SRH_TAG 0x0400 20 - #define IP6T_SRH_MASK 0x07FF 20 + #define IP6T_SRH_PSID 0x0800 21 + #define IP6T_SRH_NSID 0x1000 22 + #define IP6T_SRH_LSID 0x2000 23 + #define IP6T_SRH_MASK 0x3FFF 21 24 22 25 /* Values for "mt_invflags" field in struct ip6t_srh */ 23 26 #define IP6T_SRH_INV_NEXTHDR 0x0001 ··· 34 31 #define IP6T_SRH_INV_LAST_GT 0x0100 35 32 #define IP6T_SRH_INV_LAST_LT 0x0200 36 33 #define IP6T_SRH_INV_TAG 0x0400 37 - #define IP6T_SRH_INV_MASK 0x07FF 34 + #define IP6T_SRH_INV_PSID 0x0800 35 + #define IP6T_SRH_INV_NSID 0x1000 36 + #define IP6T_SRH_INV_LSID 0x2000 37 + #define IP6T_SRH_INV_MASK 0x3FFF 38 38 39 39 /** 40 40 * struct ip6t_srh - SRH match options ··· 56 50 __u8 segs_left; 57 51 __u8 last_entry; 58 52 __u16 tag; 53 + __u16 mt_flags; 54 + __u16 mt_invflags; 55 + }; 56 + 57 + /** 58 + * struct ip6t_srh1 - SRH match options (revision 1) 59 + * @ next_hdr: Next header field of SRH 60 + * @ hdr_len: Extension header length field of SRH 61 + * @ segs_left: Segments left field of SRH 62 + * @ last_entry: Last entry field of SRH 63 + * @ tag: Tag field of SRH 64 + * @ psid_addr: Address of previous SID in SRH SID list 65 + * @ nsid_addr: Address of NEXT SID in SRH SID list 66 + * @ lsid_addr: Address of LAST SID in SRH SID list 67 + * @ psid_msk: Mask of previous SID in SRH SID list 68 + * @ nsid_msk: Mask of next SID in SRH SID list 69 + * @ lsid_msk: MAsk of last SID in SRH SID list 70 + * @ mt_flags: match options 71 + * @ mt_invflags: Invert the sense of match options 72 + */ 73 + 74 + struct ip6t_srh1 { 75 + __u8 next_hdr; 76 + __u8 hdr_len; 77 + __u8 segs_left; 78 + __u8 last_entry; 79 + __u16 tag; 80 + struct in6_addr psid_addr; 81 + struct in6_addr nsid_addr; 82 + struct in6_addr lsid_addr; 83 + struct in6_addr psid_msk; 84 + struct in6_addr nsid_msk; 85 + struct in6_addr lsid_msk; 59 86 __u16 mt_flags; 60 87 __u16 mt_invflags; 61 88 };
+164 -9
net/ipv6/netfilter/ip6t_srh.c
··· 117 117 return true; 118 118 } 119 119 120 + static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par) 121 + { 122 + int hdrlen, psidoff, nsidoff, lsidoff, srhoff = 0; 123 + const struct ip6t_srh1 *srhinfo = par->matchinfo; 124 + struct in6_addr *psid, *nsid, *lsid; 125 + struct in6_addr _psid, _nsid, _lsid; 126 + struct ipv6_sr_hdr *srh; 127 + struct ipv6_sr_hdr _srh; 128 + 129 + if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) 130 + return false; 131 + srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh); 132 + if (!srh) 133 + return false; 134 + 135 + hdrlen = ipv6_optlen(srh); 136 + if (skb->len - srhoff < hdrlen) 137 + return false; 138 + 139 + if (srh->type != IPV6_SRCRT_TYPE_4) 140 + return false; 141 + 142 + if (srh->segments_left > srh->first_segment) 143 + return false; 144 + 145 + /* Next Header matching */ 146 + if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR) 147 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR, 148 + !(srh->nexthdr == srhinfo->next_hdr))) 149 + return false; 150 + 151 + /* Header Extension Length matching */ 152 + if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ) 153 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ, 154 + !(srh->hdrlen == srhinfo->hdr_len))) 155 + return false; 156 + if (srhinfo->mt_flags & IP6T_SRH_LEN_GT) 157 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT, 158 + !(srh->hdrlen > srhinfo->hdr_len))) 159 + return false; 160 + if (srhinfo->mt_flags & IP6T_SRH_LEN_LT) 161 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT, 162 + !(srh->hdrlen < srhinfo->hdr_len))) 163 + return false; 164 + 165 + /* Segments Left matching */ 166 + if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ) 167 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ, 168 + !(srh->segments_left == srhinfo->segs_left))) 169 + return false; 170 + if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT) 171 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT, 172 + !(srh->segments_left > srhinfo->segs_left))) 173 + return false; 174 + if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT) 175 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT, 176 + !(srh->segments_left < srhinfo->segs_left))) 177 + return false; 178 + 179 + /** 180 + * Last Entry matching 181 + * Last_Entry field was introduced in revision 6 of the SRH draft. 182 + * It was called First_Segment in the previous revision 183 + */ 184 + if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ) 185 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ, 186 + !(srh->first_segment == srhinfo->last_entry))) 187 + return false; 188 + if (srhinfo->mt_flags & IP6T_SRH_LAST_GT) 189 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT, 190 + !(srh->first_segment > srhinfo->last_entry))) 191 + return false; 192 + if (srhinfo->mt_flags & IP6T_SRH_LAST_LT) 193 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT, 194 + !(srh->first_segment < srhinfo->last_entry))) 195 + return false; 196 + 197 + /** 198 + * Tag matchig 199 + * Tag field was introduced in revision 6 of the SRH draft 200 + */ 201 + if (srhinfo->mt_flags & IP6T_SRH_TAG) 202 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG, 203 + !(srh->tag == srhinfo->tag))) 204 + return false; 205 + 206 + /* Previous SID matching */ 207 + if (srhinfo->mt_flags & IP6T_SRH_PSID) { 208 + if (srh->segments_left == srh->first_segment) 209 + return false; 210 + psidoff = srhoff + sizeof(struct ipv6_sr_hdr) + 211 + ((srh->segments_left + 1) * sizeof(struct in6_addr)); 212 + psid = skb_header_pointer(skb, psidoff, sizeof(_psid), &_psid); 213 + if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_PSID, 214 + ipv6_masked_addr_cmp(psid, &srhinfo->psid_msk, 215 + &srhinfo->psid_addr))) 216 + return false; 217 + } 218 + 219 + /* Next SID matching */ 220 + if (srhinfo->mt_flags & IP6T_SRH_NSID) { 221 + if (srh->segments_left == 0) 222 + return false; 223 + nsidoff = srhoff + sizeof(struct ipv6_sr_hdr) + 224 + ((srh->segments_left - 1) * sizeof(struct in6_addr)); 225 + nsid = skb_header_pointer(skb, nsidoff, sizeof(_nsid), &_nsid); 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 (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LSID, 237 + ipv6_masked_addr_cmp(lsid, &srhinfo->lsid_msk, 238 + &srhinfo->lsid_addr))) 239 + return false; 240 + } 241 + return true; 242 + } 243 + 120 244 static int srh_mt6_check(const struct xt_mtchk_param *par) 121 245 { 122 246 const struct ip6t_srh *srhinfo = par->matchinfo; ··· 260 136 return 0; 261 137 } 262 138 263 - static struct xt_match srh_mt6_reg __read_mostly = { 264 - .name = "srh", 265 - .family = NFPROTO_IPV6, 266 - .match = srh_mt6, 267 - .matchsize = sizeof(struct ip6t_srh), 268 - .checkentry = srh_mt6_check, 269 - .me = THIS_MODULE, 139 + static int srh1_mt6_check(const struct xt_mtchk_param *par) 140 + { 141 + const struct ip6t_srh1 *srhinfo = par->matchinfo; 142 + 143 + if (srhinfo->mt_flags & ~IP6T_SRH_MASK) { 144 + pr_info_ratelimited("unknown srh match flags %X\n", 145 + srhinfo->mt_flags); 146 + return -EINVAL; 147 + } 148 + 149 + if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) { 150 + pr_info_ratelimited("unknown srh invflags %X\n", 151 + srhinfo->mt_invflags); 152 + return -EINVAL; 153 + } 154 + 155 + return 0; 156 + } 157 + 158 + static struct xt_match srh_mt6_reg[] __read_mostly = { 159 + { 160 + .name = "srh", 161 + .revision = 0, 162 + .family = NFPROTO_IPV6, 163 + .match = srh_mt6, 164 + .matchsize = sizeof(struct ip6t_srh), 165 + .checkentry = srh_mt6_check, 166 + .me = THIS_MODULE, 167 + }, 168 + { 169 + .name = "srh", 170 + .revision = 1, 171 + .family = NFPROTO_IPV6, 172 + .match = srh1_mt6, 173 + .matchsize = sizeof(struct ip6t_srh1), 174 + .checkentry = srh1_mt6_check, 175 + .me = THIS_MODULE, 176 + } 270 177 }; 271 178 272 179 static int __init srh_mt6_init(void) 273 180 { 274 - return xt_register_match(&srh_mt6_reg); 181 + return xt_register_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg)); 275 182 } 276 183 277 184 static void __exit srh_mt6_exit(void) 278 185 { 279 - xt_unregister_match(&srh_mt6_reg); 186 + xt_unregister_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg)); 280 187 } 281 188 282 189 module_init(srh_mt6_init);