On Wed, Aug 3, 2016 at 7:52 AM, <f...@ikuai8.com> wrote: > From: Gao Feng <f...@ikuai8.com> > > The PPTP is encapsulated by GRE header with that GRE_VERSION bits > must contain one. But current GRE RPS needs the GRE_VERSION must be > zero. So RPS does not work for PPTP traffic. > > In my test environment, there are four MIPS cores, and all traffic > are passed through by PPTP. As a result, only one core is 100% busy > while other three cores are very idle. After this patch, the usage > of four cores are balanced well. > > Signed-off-by: Gao Feng <f...@ikuai8.com> > --- > v3: 1) Move struct pptp_gre_header defination into new file pptp.h > 2) Use sizeof GRE and PPTP type instead of literal value; > 3) Remove strict flag check for PPTP to robust; > 4) Consolidate the codes again; > v2: Update according to Tom and Philp's advice. > 1) Consolidate the codes with GRE version 0 path; > 2) Use PPP_PROTOCOL to get ppp protol; > 3) Set the FLOW_DIS_ENCAPSULATION flag; > v1: Intial patch > > drivers/net/ppp/pptp.c | 36 +---------- > include/net/pptp.h | 40 ++++++++++++ > include/uapi/linux/if_tunnel.h | 7 +- > net/core/flow_dissector.c | 141 > +++++++++++++++++++++++++---------------- > 4 files changed, 134 insertions(+), 90 deletions(-) > create mode 100644 include/net/pptp.h > > diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c > index ae0905e..3e68dbc 100644 > --- a/drivers/net/ppp/pptp.c > +++ b/drivers/net/ppp/pptp.c > @@ -37,6 +37,7 @@ > #include <net/icmp.h> > #include <net/route.h> > #include <net/gre.h> > +#include <net/pptp.h> > > #include <linux/uaccess.h> > > @@ -53,41 +54,6 @@ static struct proto pptp_sk_proto __read_mostly; > static const struct ppp_channel_ops pptp_chan_ops; > static const struct proto_ops pptp_ops; > > -#define PPP_LCP_ECHOREQ 0x09 > -#define PPP_LCP_ECHOREP 0x0A > -#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) > - > -#define MISSING_WINDOW 20 > -#define WRAPPED(curseq, lastseq)\ > - ((((curseq) & 0xffffff00) == 0) &&\ > - (((lastseq) & 0xffffff00) == 0xffffff00)) > - > -#define PPTP_GRE_PROTO 0x880B > -#define PPTP_GRE_VER 0x1 > - > -#define PPTP_GRE_FLAG_C 0x80 > -#define PPTP_GRE_FLAG_R 0x40 > -#define PPTP_GRE_FLAG_K 0x20 > -#define PPTP_GRE_FLAG_S 0x10 > -#define PPTP_GRE_FLAG_A 0x80 > - > -#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) > -#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) > -#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) > -#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) > -#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) > - > -#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header)) > -struct pptp_gre_header { > - u8 flags; > - u8 ver; > - __be16 protocol; > - __be16 payload_len; > - __be16 call_id; > - __be32 seq; > - __be32 ack; > -} __packed; > - > static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) > { > struct pppox_sock *sock; > diff --git a/include/net/pptp.h b/include/net/pptp.h > new file mode 100644 > index 0000000..301d3e2 > --- /dev/null > +++ b/include/net/pptp.h > @@ -0,0 +1,40 @@ > +#ifndef _NET_PPTP_H > +#define _NET_PPTP_H > + > +#define PPP_LCP_ECHOREQ 0x09 > +#define PPP_LCP_ECHOREP 0x0A > +#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) > + > +#define MISSING_WINDOW 20 > +#define WRAPPED(curseq, lastseq)\ > + ((((curseq) & 0xffffff00) == 0) &&\ > + (((lastseq) & 0xffffff00) == 0xffffff00)) > + > +#define PPTP_GRE_PROTO 0x880B > +#define PPTP_GRE_VER 0x1 > + > +#define PPTP_GRE_FLAG_C 0x80 > +#define PPTP_GRE_FLAG_R 0x40 > +#define PPTP_GRE_FLAG_K 0x20 > +#define PPTP_GRE_FLAG_S 0x10 > +#define PPTP_GRE_FLAG_A 0x80 > + > +#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) > +#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) > +#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) > +#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) > +#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) > + > +#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header)) > +struct pptp_gre_header { > + u8 flags; > + u8 ver; > + __be16 protocol; > + __be16 payload_len; > + __be16 call_id; > + __be32 seq; > + __be32 ack; > +} __packed; > + > + > +#endif > diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h > index 1046f55..7d889db 100644 > --- a/include/uapi/linux/if_tunnel.h > +++ b/include/uapi/linux/if_tunnel.h > @@ -24,9 +24,14 @@ > #define GRE_SEQ __cpu_to_be16(0x1000) > #define GRE_STRICT __cpu_to_be16(0x0800) > #define GRE_REC __cpu_to_be16(0x0700) > -#define GRE_FLAGS __cpu_to_be16(0x00F8) > +#define GRE_ACK __cpu_to_be16(0x0080) > +#define GRE_FLAGS __cpu_to_be16(0x0078) > #define GRE_VERSION __cpu_to_be16(0x0007) > > +#define GRE_VERSION_1 __cpu_to_be16(0x0001) > +#define GRE_PROTO_PPP __cpu_to_be16(0x880b) > + > + > struct ip_tunnel_parm { > char name[IFNAMSIZ]; > int link; > diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c > index 61ad43f..52b7c3c 100644 > --- a/net/core/flow_dissector.c > +++ b/net/core/flow_dissector.c > @@ -6,6 +6,8 @@ > #include <linux/if_vlan.h> > #include <net/ip.h> > #include <net/ipv6.h> > +#include <net/gre.h> > +#include <net/pptp.h> > #include <linux/igmp.h> > #include <linux/icmp.h> > #include <linux/sctp.h> > @@ -338,71 +340,102 @@ mpls: > ip_proto_again: > switch (ip_proto) { > case IPPROTO_GRE: { > - struct gre_hdr { > - __be16 flags; > - __be16 proto; > - } *hdr, _hdr; > + struct gre_base_hdr *hdr, _hdr; > > hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, > hlen, &_hdr); > if (!hdr) > goto out_bad; > - /* > - * Only look inside GRE if version zero and no > - * routing > - */ > - if (hdr->flags & (GRE_VERSION | GRE_ROUTING)) > - break; > > - proto = hdr->proto; > - nhoff += 4; > - if (hdr->flags & GRE_CSUM) > - nhoff += 4; > - if (hdr->flags & GRE_KEY) { > - const __be32 *keyid; > - __be32 _keyid; > + /* Only look inside GRE without routing */ > + if (!(hdr->flags & GRE_ROUTING)) { > + int offset = 0; > > - keyid = __skb_header_pointer(skb, nhoff, > sizeof(_keyid), > - data, hlen, &_keyid); > + proto = hdr->protocol; > > - if (!keyid) > - goto out_bad; > + if (hdr->flags & GRE_VERSION) { > + /* Maybe PPTP in GRE */ > + if (!(proto == GRE_PROTO_PPP && (hdr->flags & > GRE_KEY) && > + (hdr->flags & GRE_VERSION) == > GRE_VERSION_1)) > + break; > + } > > - if (dissector_uses_key(flow_dissector, > - FLOW_DISSECTOR_KEY_GRE_KEYID)) > { > - key_keyid = > skb_flow_dissector_target(flow_dissector, > - > FLOW_DISSECTOR_KEY_GRE_KEYID, > - > target_container); > - key_keyid->keyid = *keyid; > + offset += sizeof(struct gre_base_hdr); > + > + if (hdr->flags & GRE_CSUM) > + offset += sizeof(__be32); > + > + if (hdr->flags & GRE_KEY) { > + const __be32 *keyid; > + __be32 _keyid; > + > + keyid = __skb_header_pointer(skb, nhoff + > offset, sizeof(_keyid), > + data, hlen, > &_keyid); > + > + if (!keyid) > + goto out_bad; > + > + if (dissector_uses_key(flow_dissector, > + > FLOW_DISSECTOR_KEY_GRE_KEYID)) { > + key_keyid = > skb_flow_dissector_target(flow_dissector, > + > FLOW_DISSECTOR_KEY_GRE_KEYID, > + > target_container); > + key_keyid->keyid = *keyid; > + } > + offset += sizeof(_keyid); > } > - nhoff += 4; > - } > - if (hdr->flags & GRE_SEQ) > - nhoff += 4; > - if (proto == htons(ETH_P_TEB)) { > - const struct ethhdr *eth; > - struct ethhdr _eth; > - > - eth = __skb_header_pointer(skb, nhoff, > - sizeof(_eth), > - data, hlen, &_eth); > - if (!eth) > - goto out_bad; > - proto = eth->h_proto; > - nhoff += sizeof(*eth); > - > - /* Cap headers that we access via pointers at the > - * end of the Ethernet header as our maximum alignment > - * at that point is only 2 bytes. > - */ > - if (NET_IP_ALIGN) > - hlen = nhoff; > - } > > - key_control->flags |= FLOW_DIS_ENCAPSULATION; > - if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) > - goto out_good; > + if (hdr->flags & GRE_SEQ) > + offset += sizeof(((struct pptp_gre_header > *)0)->seq); > + > + if (hdr->flags & GRE_ACK) > + offset += sizeof(((struct pptp_gre_header > *)0)->ack); > + > + if (proto == GRE_PROTO_PPP) { > + u8 _ppp_hdr[PPP_HDRLEN]; > + u8 *ppp_hdr; > + > + ppp_hdr = skb_header_pointer(skb, nhoff + > offset, > + > sizeof(_ppp_hdr), _ppp_hdr); > + if (!ppp_hdr) > + goto out_bad; > + > + proto = PPP_PROTOCOL(ppp_hdr); > + if (proto == PPP_IP) > + proto = htons(ETH_P_IP); > + else if (proto == PPP_IPV6) > + proto = htons(ETH_P_IPV6); > + else > + break; > + > + offset += PPP_HDRLEN; > + } else if (proto == htons(ETH_P_TEB)) { > + const struct ethhdr *eth; > + struct ethhdr _eth; > + > + eth = __skb_header_pointer(skb, nhoff + > offset, > + sizeof(_eth), > + data, hlen, &_eth); > + if (!eth) > + goto out_bad; > + proto = eth->h_proto; > + offset += sizeof(*eth); > + > + /* Cap headers that we access via pointers at > the > + * end of the Ethernet header as our maximum > alignment > + * at that point is only 2 bytes. > + */ > + if (NET_IP_ALIGN) > + hlen = (nhoff + offset); > + } > > - goto again; > + nhoff += offset; > + key_control->flags |= FLOW_DIS_ENCAPSULATION; > + if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) > + goto out_good; > + > + goto again; > + } > + break; > } > case NEXTHDR_HOP: > case NEXTHDR_ROUTING: > -- > 1.9.1 >
HI Feng, Please be careful about order of processing GRE options, keyid must be handled first. GRE_ACK looks like the only new field that needs to be considered for v1. Also, the keyid in v1 is split into two 16 bit fields; the first is payload length which is not usable for entropy, but the second (Call ID) does look useful for that. I would suggest the IPPROTO_GRE could look something like: diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 61ad43f..736b531 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -342,15 +342,20 @@ ip_proto_again: __be16 flags; __be16 proto; } *hdr, _hdr; + u8 gre_ver; hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr); if (!hdr) goto out_bad; + + /* Only look inside GRE for versions 0 and 1 */ + gre_ver = hdr->flags & GRE_VERSION; + if (gre_ver > 1) + break; /* - * Only look inside GRE if version zero and no - * routing + * Only look inside GRE if no routing */ - if (hdr->flags & (GRE_VERSION | GRE_ROUTING)) + if (hdr->flags & GRE_ROUTING) break; proto = hdr->proto; @@ -372,30 +377,62 @@ ip_proto_again: key_keyid = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_GRE_KEYID, target_container); - key_keyid->keyid = *keyid; + if (gre_ver == 0) + key_keyid->keyid = *keyid; + else + key_keyid->keyid = *keyid & htonl(0xffff); } nhoff += 4; } if (hdr->flags & GRE_SEQ) nhoff += 4; - if (proto == htons(ETH_P_TEB)) { - const struct ethhdr *eth; - struct ethhdr _eth; - - eth = __skb_header_pointer(skb, nhoff, - sizeof(_eth), - data, hlen, &_eth); - if (!eth) - goto out_bad; - proto = eth->h_proto; - nhoff += sizeof(*eth); - - /* Cap headers that we access via pointers at the - * end of the Ethernet header as our maximum alignment - * at that point is only 2 bytes. - */ - if (NET_IP_ALIGN) - hlen = nhoff; + if (gre_ver == 0) { + if (proto == htons(ETH_P_TEB)) { + const struct ethhdr *eth; + struct ethhdr _eth; + + eth = __skb_header_pointer(skb, nhoff, + sizeof(_eth), + data, hlen, &_eth); + if (!eth) + goto out_bad; + proto = eth->h_proto; + nhoff += sizeof(*eth); + + /* Cap headers that we access via pointers at the + * end of the Ethernet header as our maximum alignment + * at that point is only 2 bytes. + */ + if (NET_IP_ALIGN) + hlen = nhoff; + } + } else { /* Version 1 */ + if (hdr->flags & GRE_ACK) + offset += 4; + + if (proto == GRE_PROTO_PPP) { + u8 _ppp_hdr[PPP_HDRLEN]; + u8 *ppp_hdr; + + ppp_hdr = skb_header_pointer(skb, nhoff + offset, + sizeof(_ppp_hdr), _ppp_hdr); + if (!ppp_hdr) + goto out_bad; + + switch (PPP_PROTOCOL(ppp_hdr)); + case PPP_IP: + proto = htons(ETH_P_IP); + break; + case PPP_IPV6: + proto = htons(ETH_P_IPV6); + break; + default: + /* Could probably catch some more like MPLS */ + break; + } + + offset += PPP_HDRLEN; + } } key_control->flags |= FLOW_DIS_ENCAPSULATION;