diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index defbde2..2f80fa0 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -333,6 +333,11 @@ enum { FLOW_KEY_SKGID, FLOW_KEY_VLAN_TAG, FLOW_KEY_RXHASH, + FLOW_KEY_TUNNEL_SRC, + FLOW_KEY_TUNNEL_DST, + FLOW_KEY_TUNNEL_PROTO, + FLOW_KEY_TUNNEL_PROTO_SRC, + FLOW_KEY_TUNNEL_PROTO_DST, __FLOW_KEY_MAX, }; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 6994214..f0bd3ad 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -311,6 +311,301 @@ static u32 flow_get_rxhash(struct sk_buff *skb) return skb_get_rxhash(skb); } +static u32 tunnel_inner_ip_src(struct sk_buff *skb) +{ + if (pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct iphdr))) { + return ntohl(ipip_hdr(skb)->saddr); + } + + return 0; +} + +static u32 tunnel_inner_ipv6_src(struct sk_buff *skb) +{ + if (pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct ipv6hdr))) { + struct ipv6hdr *iph = (struct ipv6hdr *) + skb_transport_header(skb); + return ntohl(iph->saddr.s6_addr32[3]); + } + + return 0; +} + +static u32 flow_get_tunnel_src(struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (pskb_network_may_pull(skb, sizeof(struct iphdr))) { + if (ip_hdr(skb)->protocol == IPPROTO_IPIP) { + return tunnel_inner_ip_src(skb); + } else if (ip_hdr(skb)->protocol == IPPROTO_IPV6) { + return tunnel_inner_ipv6_src(skb); + } + } + break; + case htons(ETH_P_IPV6): + if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) { + if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPIP) { + return tunnel_inner_ip_src(skb); + } else if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPV6) { + return tunnel_inner_ipv6_src(skb); + } + } + break; + } + + return 0; +} + +static u32 tunnel_inner_ip_dst(struct sk_buff *skb) +{ + if (pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct iphdr))) { + return ntohl(ipip_hdr(skb)->daddr); + } + + return 0; +} + +static u32 tunnel_inner_ipv6_dst(struct sk_buff *skb) +{ + if (pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct ipv6hdr))) { + struct ipv6hdr *iph = (struct ipv6hdr *) + skb_transport_header(skb); + return ntohl(iph->daddr.s6_addr32[3]); + } + + return 0; +} + +static u32 flow_get_tunnel_dst(struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (pskb_network_may_pull(skb, sizeof(struct iphdr))) { + if (ip_hdr(skb)->protocol == IPPROTO_IPIP) { + return tunnel_inner_ip_dst(skb); + } else if (ip_hdr(skb)->protocol == IPPROTO_IPV6) { + return tunnel_inner_ipv6_dst(skb); + } + } + break; + case htons(ETH_P_IPV6): + if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) { + if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPIP) { + return tunnel_inner_ip_dst(skb); + } else if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPV6) { + return tunnel_inner_ipv6_dst(skb); + } + } + break; + } + + return 0; +} + +static u32 tunnel_inner_ip_proto(struct sk_buff *skb) +{ + struct iphdr *iph; + + if (!pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct iphdr))) { + return 0; + } + + iph = ipip_hdr(skb); + + return iph->protocol; +} + +static u32 tunnel_inner_ipv6_proto(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + + if (!pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct ipv6hdr))) { + return 0; + } + + ipv6h = (struct ipv6hdr *)skb_transport_header(skb); + + return ipv6h->nexthdr; +} + +static u32 flow_get_tunnel_proto(struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (pskb_network_may_pull(skb, sizeof(struct iphdr))) { + if (ip_hdr(skb)->protocol == IPPROTO_IPIP) { + return tunnel_inner_ip_proto(skb); + } else if (ip_hdr(skb)->protocol == IPPROTO_IPV6) { + return tunnel_inner_ipv6_proto(skb); + } + } + break; + case htons(ETH_P_IPV6): + if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) { + if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPIP) { + return tunnel_inner_ip_proto(skb); + } else if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPV6) { + return tunnel_inner_ipv6_proto(skb); + } + } + break; + } + + return 0; +} + +static u32 tunnel_inner_ip_proto_src(struct sk_buff *skb) +{ + struct iphdr *iph; + int poff; + + if (!pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct iphdr))) { + return 0; + } + + iph = ipip_hdr(skb); + + if (ip_is_fragment(iph)) + return 0; + + poff = proto_ports_offset(iph->protocol); + if (poff >= 0 && pskb_network_may_pull(skb, skb_network_header_len(skb) + + iph->ihl * 4 + 2 + poff)) { + return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + poff)); + } + + return 0; +} + +static u32 tunnel_inner_ipv6_proto_src(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + int poff; + + if (!pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct ipv6hdr))) { + return 0; + } + + ipv6h = (struct ipv6hdr *)skb_transport_header(skb); + + poff = proto_ports_offset(ipv6h->nexthdr); + if (poff >= 0 && + pskb_network_may_pull(skb, sizeof(*ipv6h) + poff + 2)) { + return ntohs(*(__be16 *)((void *)ipv6h + sizeof(*ipv6h) + + poff)); + } + + return 0; +} + +static u32 flow_get_tunnel_proto_src(struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (pskb_network_may_pull(skb, sizeof(struct iphdr))) { + if (ip_hdr(skb)->protocol == IPPROTO_IPIP) { + return tunnel_inner_ip_proto_src(skb); + } else if (ip_hdr(skb)->protocol == IPPROTO_IPV6) { + return tunnel_inner_ipv6_proto_src(skb); + } + return 0; + } + break; + case htons(ETH_P_IPV6): + if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) { + if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPIP) { + return tunnel_inner_ip_proto_src(skb); + } else if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPV6) { + return tunnel_inner_ipv6_proto_src(skb); + } + } + break; + } + + return 0; +} + +static u32 tunnel_inner_ip_proto_dst(struct sk_buff *skb) +{ + struct iphdr *iph; + int poff; + + if (!pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct iphdr))) { + return 0; + } + + iph = ipip_hdr(skb); + + if (ip_is_fragment(iph)) + return 0; + + poff = proto_ports_offset(iph->protocol); + if (poff >= 0 && pskb_network_may_pull(skb, skb_network_header_len(skb) + + iph->ihl * 4 + 4 + poff)) { + return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + 2 + poff)); + } + + return 0; +} + +static u32 tunnel_inner_ipv6_proto_dst(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + int poff; + + if (!pskb_network_may_pull(skb, skb_network_header_len(skb) + + sizeof(struct ipv6hdr))) { + return 0; + } + + ipv6h = (struct ipv6hdr *)skb_transport_header(skb); + + poff = proto_ports_offset(ipv6h->nexthdr); + if (poff >= 0 && + pskb_network_may_pull(skb, sizeof(*ipv6h) + poff + 4)) { + return ntohs(*(__be16 *)((void *)ipv6h + sizeof(*ipv6h) + + poff + 2)); + } + + return 0; +} + +static u32 flow_get_tunnel_proto_dst(struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (pskb_network_may_pull(skb, sizeof(struct iphdr))) { + if (ip_hdr(skb)->protocol == IPPROTO_IPIP) { + return tunnel_inner_ip_proto_dst(skb); + } else if (ip_hdr(skb)->protocol == IPPROTO_IPV6) { + return tunnel_inner_ipv6_proto_dst(skb); + } + } + break; + case htons(ETH_P_IPV6): + if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) { + if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPIP) { + return tunnel_inner_ip_proto_dst(skb); + } else if (ipv6_hdr(skb)->nexthdr == IPPROTO_IPV6) { + return tunnel_inner_ipv6_proto_dst(skb); + } + } + break; + } + + return 0; +} + static u32 flow_key_get(struct sk_buff *skb, int key) { switch (key) { @@ -350,6 +645,16 @@ static u32 flow_key_get(struct sk_buff *skb, int key) return flow_get_vlan_tag(skb); case FLOW_KEY_RXHASH: return flow_get_rxhash(skb); + case FLOW_KEY_TUNNEL_SRC: + return flow_get_tunnel_src(skb); + case FLOW_KEY_TUNNEL_DST: + return flow_get_tunnel_dst(skb); + case FLOW_KEY_TUNNEL_PROTO: + return flow_get_tunnel_proto(skb); + case FLOW_KEY_TUNNEL_PROTO_SRC: + return flow_get_tunnel_proto_src(skb); + case FLOW_KEY_TUNNEL_PROTO_DST: + return flow_get_tunnel_proto_dst(skb); default: WARN_ON(1); return 0;