Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab10dccb authored by Gao Feng's avatar Gao Feng Committed by David S. Miller
Browse files

rps: Inspect PPTP encapsulated by GRE to get flow hash



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: default avatarGao Feng <fgao@ikuai8.com>
Reviewed-by: default avatarPhilip Prindeville <philipp@redfish-solutions.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 084c9535
Loading
Loading
Loading
Loading
+1 −35
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@
#include <net/icmp.h>
#include <net/icmp.h>
#include <net/route.h>
#include <net/route.h>
#include <net/gre.h>
#include <net/gre.h>
#include <net/pptp.h>


#include <linux/uaccess.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 ppp_channel_ops pptp_chan_ops;
static const struct proto_ops pptp_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)
static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
{
{
	struct pppox_sock *sock;
	struct pppox_sock *sock;
+9 −1
Original line number Original line Diff line number Diff line
@@ -7,7 +7,15 @@
struct gre_base_hdr {
struct gre_base_hdr {
	__be16 flags;
	__be16 flags;
	__be16 protocol;
	__be16 protocol;
};
} __packed;

struct gre_full_hdr {
	struct gre_base_hdr fixed_header;
	__be16 csum;
	__be16 reserved1;
	__be32 key;
	__be32 seq;
} __packed;
#define GRE_HEADER_SECTION 4
#define GRE_HEADER_SECTION 4


#define GREPROTO_CISCO		0
#define GREPROTO_CISCO		0

include/net/pptp.h

0 → 100644
+40 −0
Original line number Original line Diff line number Diff line
#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
+6 −1
Original line number Original line Diff line number Diff line
@@ -24,9 +24,14 @@
#define GRE_SEQ		__cpu_to_be16(0x1000)
#define GRE_SEQ		__cpu_to_be16(0x1000)
#define GRE_STRICT	__cpu_to_be16(0x0800)
#define GRE_STRICT	__cpu_to_be16(0x0800)
#define GRE_REC		__cpu_to_be16(0x0700)
#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	__cpu_to_be16(0x0007)


#define GRE_VERSION_1	__cpu_to_be16(0x0001)
#define GRE_PROTO_PPP	__cpu_to_be16(0x880b)
#define GRE_PPTP_KEY_MASK	__cpu_to_be32(0xffff)

struct ip_tunnel_parm {
struct ip_tunnel_parm {
	char			name[IFNAMSIZ];
	char			name[IFNAMSIZ];
	int			link;
	int			link;
+79 −34
Original line number Original line Diff line number Diff line
@@ -6,6 +6,8 @@
#include <linux/if_vlan.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ipv6.h>
#include <net/gre.h>
#include <net/pptp.h>
#include <linux/igmp.h>
#include <linux/igmp.h>
#include <linux/icmp.h>
#include <linux/icmp.h>
#include <linux/sctp.h>
#include <linux/sctp.h>
@@ -338,32 +340,42 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
ip_proto_again:
ip_proto_again:
	switch (ip_proto) {
	switch (ip_proto) {
	case IPPROTO_GRE: {
	case IPPROTO_GRE: {
		struct gre_hdr {
		struct gre_base_hdr *hdr, _hdr;
			__be16 flags;
		u16 gre_ver;
			__be16 proto;
		int offset = 0;
		} *hdr, _hdr;


		hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
		hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
		if (!hdr)
		if (!hdr)
			goto out_bad;
			goto out_bad;
		/*

		 * Only look inside GRE if version zero and no
		/* Only look inside GRE without routing */
		 * routing
		if (hdr->flags & GRE_ROUTING)
		 */
		if (hdr->flags & (GRE_VERSION | GRE_ROUTING))
			break;
			break;


		proto = hdr->proto;
		/* Only look inside GRE for version 0 and 1 */
		nhoff += 4;
		gre_ver = ntohs(hdr->flags & GRE_VERSION);
		if (gre_ver > 1)
			break;

		proto = hdr->protocol;
		if (gre_ver) {
			/* Version1 must be PPTP, and check the flags */
			if (!(proto == GRE_PROTO_PPP && (hdr->flags & GRE_KEY)))
				break;
		}

		offset += sizeof(struct gre_base_hdr);

		if (hdr->flags & GRE_CSUM)
		if (hdr->flags & GRE_CSUM)
			nhoff += 4;
			offset += sizeof(((struct gre_full_hdr *)0)->csum) +
				  sizeof(((struct gre_full_hdr *)0)->reserved1);

		if (hdr->flags & GRE_KEY) {
		if (hdr->flags & GRE_KEY) {
			const __be32 *keyid;
			const __be32 *keyid;
			__be32 _keyid;
			__be32 _keyid;


			keyid = __skb_header_pointer(skb, nhoff, sizeof(_keyid),
			keyid = __skb_header_pointer(skb, nhoff + offset, sizeof(_keyid),
						     data, hlen, &_keyid);
						     data, hlen, &_keyid);

			if (!keyid)
			if (!keyid)
				goto out_bad;
				goto out_bad;


@@ -372,32 +384,65 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
				key_keyid = skb_flow_dissector_target(flow_dissector,
				key_keyid = skb_flow_dissector_target(flow_dissector,
								      FLOW_DISSECTOR_KEY_GRE_KEYID,
								      FLOW_DISSECTOR_KEY_GRE_KEYID,
								      target_container);
								      target_container);
				if (gre_ver == 0)
					key_keyid->keyid = *keyid;
					key_keyid->keyid = *keyid;
				else
					key_keyid->keyid = *keyid & GRE_PPTP_KEY_MASK;
			}
			}
			nhoff += 4;
			offset += sizeof(((struct gre_full_hdr *)0)->key);
		}
		}

		if (hdr->flags & GRE_SEQ)
		if (hdr->flags & GRE_SEQ)
			nhoff += 4;
			offset += sizeof(((struct pptp_gre_header *)0)->seq);

		if (gre_ver == 0) {
			if (proto == htons(ETH_P_TEB)) {
			if (proto == htons(ETH_P_TEB)) {
				const struct ethhdr *eth;
				const struct ethhdr *eth;
				struct ethhdr _eth;
				struct ethhdr _eth;


			eth = __skb_header_pointer(skb, nhoff,
				eth = __skb_header_pointer(skb, nhoff + offset,
							   sizeof(_eth),
							   sizeof(_eth),
							   data, hlen, &_eth);
							   data, hlen, &_eth);
				if (!eth)
				if (!eth)
					goto out_bad;
					goto out_bad;
				proto = eth->h_proto;
				proto = eth->h_proto;
			nhoff += sizeof(*eth);
				offset += sizeof(*eth);


				/* Cap headers that we access via pointers at the
				/* Cap headers that we access via pointers at the
				 * end of the Ethernet header as our maximum alignment
				 * end of the Ethernet header as our maximum alignment
				 * at that point is only 2 bytes.
				 * at that point is only 2 bytes.
				 */
				 */
				if (NET_IP_ALIGN)
				if (NET_IP_ALIGN)
				hlen = nhoff;
					hlen = (nhoff + offset);
			}
		} else { /* version 1, must be PPTP */
			u8 _ppp_hdr[PPP_HDRLEN];
			u8 *ppp_hdr;

			if (hdr->flags & GRE_ACK)
				offset += sizeof(((struct pptp_gre_header *)0)->ack);

			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;
		}
		}


		nhoff += offset;
		key_control->flags |= FLOW_DIS_ENCAPSULATION;
		key_control->flags |= FLOW_DIS_ENCAPSULATION;
		if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
		if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
			goto out_good;
			goto out_good;