* [PATCH 3/3] NETFILTER: New PPTP helper
From: Harald Welte @ 2005-09-15 14:54 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1: Type: text/plain, Size: 73223 bytes --]
Hi Dave, please push this to mainline. Thanks!
NETFILTER: Add new PPTP conntrack and NAT helper
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h
--- a/include/linux/netfilter_ipv4/ip_conntrack.h
+++ b/include/linux/netfilter_ipv4/ip_conntrack.h
@@ -133,11 +133,13 @@ enum ip_conntrack_expect_events {
#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
#include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
#include <linux/netfilter_ipv4/ip_conntrack_sctp.h>
/* per conntrack: protocol private data */
union ip_conntrack_proto {
/* insert conntrack proto private data here */
+ struct ip_ct_gre gre;
struct ip_ct_sctp sctp;
struct ip_ct_tcp tcp;
struct ip_ct_icmp icmp;
@@ -148,6 +150,7 @@ union ip_conntrack_expect_proto {
};
/* Add protocol helper include file here */
+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
@@ -155,12 +158,20 @@ union ip_conntrack_expect_proto {
/* per conntrack: application helper private data */
union ip_conntrack_help {
/* insert conntrack helper private data (master) here */
+ struct ip_ct_pptp_master ct_pptp_info;
struct ip_ct_ftp_master ct_ftp_info;
struct ip_ct_irc_master ct_irc_info;
};
#ifdef CONFIG_IP_NF_NAT_NEEDED
#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_pptp.h>
+
+/* per conntrack: nat application helper private data */
+union ip_conntrack_nat_help {
+ /* insert nat helper private data here */
+ struct ip_nat_pptp nat_pptp_info;
+};
#endif
#include <linux/types.h>
@@ -223,6 +234,7 @@ struct ip_conntrack
#ifdef CONFIG_IP_NF_NAT_NEEDED
struct {
struct ip_nat_info info;
+ union ip_conntrack_nat_help help;
#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
int masq_index;
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
@@ -0,0 +1,332 @@
+/* PPTP constants and structs */
+#ifndef _CONNTRACK_PPTP_H
+#define _CONNTRACK_PPTP_H
+
+/* state of the control session */
+enum pptp_ctrlsess_state {
+ PPTP_SESSION_NONE, /* no session present */
+ PPTP_SESSION_ERROR, /* some session error */
+ PPTP_SESSION_STOPREQ, /* stop_sess request seen */
+ PPTP_SESSION_REQUESTED, /* start_sess request seen */
+ PPTP_SESSION_CONFIRMED, /* session established */
+};
+
+/* state of the call inside the control session */
+enum pptp_ctrlcall_state {
+ PPTP_CALL_NONE,
+ PPTP_CALL_ERROR,
+ PPTP_CALL_OUT_REQ,
+ PPTP_CALL_OUT_CONF,
+ PPTP_CALL_IN_REQ,
+ PPTP_CALL_IN_REP,
+ PPTP_CALL_IN_CONF,
+ PPTP_CALL_CLEAR_REQ,
+};
+
+
+/* conntrack private data */
+struct ip_ct_pptp_master {
+ enum pptp_ctrlsess_state sstate; /* session state */
+
+ /* everything below is going to be per-expectation in newnat,
+ * since there could be more than one call within one session */
+ enum pptp_ctrlcall_state cstate; /* call state */
+ u_int16_t pac_call_id; /* call id of PAC, host byte order */
+ u_int16_t pns_call_id; /* call id of PNS, host byte order */
+
+ /* in pre-2.6.11 this used to be per-expect. Now it is per-conntrack
+ * and therefore imposes a fixed limit on the number of maps */
+ struct ip_ct_gre_keymap *keymap_orig, *keymap_reply;
+};
+
+/* conntrack_expect private member */
+struct ip_ct_pptp_expect {
+ enum pptp_ctrlcall_state cstate; /* call state */
+ u_int16_t pac_call_id; /* call id of PAC */
+ u_int16_t pns_call_id; /* call id of PNS */
+};
+
+
+#ifdef __KERNEL__
+
+#define IP_CONNTR_PPTP PPTP_CONTROL_PORT
+
+#define PPTP_CONTROL_PORT 1723
+
+#define PPTP_PACKET_CONTROL 1
+#define PPTP_PACKET_MGMT 2
+
+#define PPTP_MAGIC_COOKIE 0x1a2b3c4d
+
+struct pptp_pkt_hdr {
+ __u16 packetLength;
+ __u16 packetType;
+ __u32 magicCookie;
+};
+
+/* PptpControlMessageType values */
+#define PPTP_START_SESSION_REQUEST 1
+#define PPTP_START_SESSION_REPLY 2
+#define PPTP_STOP_SESSION_REQUEST 3
+#define PPTP_STOP_SESSION_REPLY 4
+#define PPTP_ECHO_REQUEST 5
+#define PPTP_ECHO_REPLY 6
+#define PPTP_OUT_CALL_REQUEST 7
+#define PPTP_OUT_CALL_REPLY 8
+#define PPTP_IN_CALL_REQUEST 9
+#define PPTP_IN_CALL_REPLY 10
+#define PPTP_IN_CALL_CONNECT 11
+#define PPTP_CALL_CLEAR_REQUEST 12
+#define PPTP_CALL_DISCONNECT_NOTIFY 13
+#define PPTP_WAN_ERROR_NOTIFY 14
+#define PPTP_SET_LINK_INFO 15
+
+#define PPTP_MSG_MAX 15
+
+/* PptpGeneralError values */
+#define PPTP_ERROR_CODE_NONE 0
+#define PPTP_NOT_CONNECTED 1
+#define PPTP_BAD_FORMAT 2
+#define PPTP_BAD_VALUE 3
+#define PPTP_NO_RESOURCE 4
+#define PPTP_BAD_CALLID 5
+#define PPTP_REMOVE_DEVICE_ERROR 6
+
+struct PptpControlHeader {
+ __u16 messageType;
+ __u16 reserved;
+};
+
+/* FramingCapability Bitmap Values */
+#define PPTP_FRAME_CAP_ASYNC 0x1
+#define PPTP_FRAME_CAP_SYNC 0x2
+
+/* BearerCapability Bitmap Values */
+#define PPTP_BEARER_CAP_ANALOG 0x1
+#define PPTP_BEARER_CAP_DIGITAL 0x2
+
+struct PptpStartSessionRequest {
+ __u16 protocolVersion;
+ __u8 reserved1;
+ __u8 reserved2;
+ __u32 framingCapability;
+ __u32 bearerCapability;
+ __u16 maxChannels;
+ __u16 firmwareRevision;
+ __u8 hostName[64];
+ __u8 vendorString[64];
+};
+
+/* PptpStartSessionResultCode Values */
+#define PPTP_START_OK 1
+#define PPTP_START_GENERAL_ERROR 2
+#define PPTP_START_ALREADY_CONNECTED 3
+#define PPTP_START_NOT_AUTHORIZED 4
+#define PPTP_START_UNKNOWN_PROTOCOL 5
+
+struct PptpStartSessionReply {
+ __u16 protocolVersion;
+ __u8 resultCode;
+ __u8 generalErrorCode;
+ __u32 framingCapability;
+ __u32 bearerCapability;
+ __u16 maxChannels;
+ __u16 firmwareRevision;
+ __u8 hostName[64];
+ __u8 vendorString[64];
+};
+
+/* PptpStopReasons */
+#define PPTP_STOP_NONE 1
+#define PPTP_STOP_PROTOCOL 2
+#define PPTP_STOP_LOCAL_SHUTDOWN 3
+
+struct PptpStopSessionRequest {
+ __u8 reason;
+};
+
+/* PptpStopSessionResultCode */
+#define PPTP_STOP_OK 1
+#define PPTP_STOP_GENERAL_ERROR 2
+
+struct PptpStopSessionReply {
+ __u8 resultCode;
+ __u8 generalErrorCode;
+};
+
+struct PptpEchoRequest {
+ __u32 identNumber;
+};
+
+/* PptpEchoReplyResultCode */
+#define PPTP_ECHO_OK 1
+#define PPTP_ECHO_GENERAL_ERROR 2
+
+struct PptpEchoReply {
+ __u32 identNumber;
+ __u8 resultCode;
+ __u8 generalErrorCode;
+ __u16 reserved;
+};
+
+/* PptpFramingType */
+#define PPTP_ASYNC_FRAMING 1
+#define PPTP_SYNC_FRAMING 2
+#define PPTP_DONT_CARE_FRAMING 3
+
+/* PptpCallBearerType */
+#define PPTP_ANALOG_TYPE 1
+#define PPTP_DIGITAL_TYPE 2
+#define PPTP_DONT_CARE_BEARER_TYPE 3
+
+struct PptpOutCallRequest {
+ __u16 callID;
+ __u16 callSerialNumber;
+ __u32 minBPS;
+ __u32 maxBPS;
+ __u32 bearerType;
+ __u32 framingType;
+ __u16 packetWindow;
+ __u16 packetProcDelay;
+ __u16 reserved1;
+ __u16 phoneNumberLength;
+ __u16 reserved2;
+ __u8 phoneNumber[64];
+ __u8 subAddress[64];
+};
+
+/* PptpCallResultCode */
+#define PPTP_OUTCALL_CONNECT 1
+#define PPTP_OUTCALL_GENERAL_ERROR 2
+#define PPTP_OUTCALL_NO_CARRIER 3
+#define PPTP_OUTCALL_BUSY 4
+#define PPTP_OUTCALL_NO_DIAL_TONE 5
+#define PPTP_OUTCALL_TIMEOUT 6
+#define PPTP_OUTCALL_DONT_ACCEPT 7
+
+struct PptpOutCallReply {
+ __u16 callID;
+ __u16 peersCallID;
+ __u8 resultCode;
+ __u8 generalErrorCode;
+ __u16 causeCode;
+ __u32 connectSpeed;
+ __u16 packetWindow;
+ __u16 packetProcDelay;
+ __u32 physChannelID;
+};
+
+struct PptpInCallRequest {
+ __u16 callID;
+ __u16 callSerialNumber;
+ __u32 callBearerType;
+ __u32 physChannelID;
+ __u16 dialedNumberLength;
+ __u16 dialingNumberLength;
+ __u8 dialedNumber[64];
+ __u8 dialingNumber[64];
+ __u8 subAddress[64];
+};
+
+/* PptpInCallResultCode */
+#define PPTP_INCALL_ACCEPT 1
+#define PPTP_INCALL_GENERAL_ERROR 2
+#define PPTP_INCALL_DONT_ACCEPT 3
+
+struct PptpInCallReply {
+ __u16 callID;
+ __u16 peersCallID;
+ __u8 resultCode;
+ __u8 generalErrorCode;
+ __u16 packetWindow;
+ __u16 packetProcDelay;
+ __u16 reserved;
+};
+
+struct PptpInCallConnected {
+ __u16 peersCallID;
+ __u16 reserved;
+ __u32 connectSpeed;
+ __u16 packetWindow;
+ __u16 packetProcDelay;
+ __u32 callFramingType;
+};
+
+struct PptpClearCallRequest {
+ __u16 callID;
+ __u16 reserved;
+};
+
+struct PptpCallDisconnectNotify {
+ __u16 callID;
+ __u8 resultCode;
+ __u8 generalErrorCode;
+ __u16 causeCode;
+ __u16 reserved;
+ __u8 callStatistics[128];
+};
+
+struct PptpWanErrorNotify {
+ __u16 peersCallID;
+ __u16 reserved;
+ __u32 crcErrors;
+ __u32 framingErrors;
+ __u32 hardwareOverRuns;
+ __u32 bufferOverRuns;
+ __u32 timeoutErrors;
+ __u32 alignmentErrors;
+};
+
+struct PptpSetLinkInfo {
+ __u16 peersCallID;
+ __u16 reserved;
+ __u32 sendAccm;
+ __u32 recvAccm;
+};
+
+
+struct pptp_priv_data {
+ __u16 call_id;
+ __u16 mcall_id;
+ __u16 pcall_id;
+};
+
+union pptp_ctrl_union {
+ struct PptpStartSessionRequest sreq;
+ struct PptpStartSessionReply srep;
+ struct PptpStopSessionRequest streq;
+ struct PptpStopSessionReply strep;
+ struct PptpOutCallRequest ocreq;
+ struct PptpOutCallReply ocack;
+ struct PptpInCallRequest icreq;
+ struct PptpInCallReply icack;
+ struct PptpInCallConnected iccon;
+ struct PptpClearCallRequest clrreq;
+ struct PptpCallDisconnectNotify disc;
+ struct PptpWanErrorNotify wanerr;
+ struct PptpSetLinkInfo setlink;
+};
+
+extern int
+(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct PptpControlHeader *ctlh,
+ union pptp_ctrl_union *pptpReq);
+
+extern int
+(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct PptpControlHeader *ctlh,
+ union pptp_ctrl_union *pptpReq);
+
+extern int
+(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *exp_orig,
+ struct ip_conntrack_expect *exp_reply);
+
+extern void
+(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct,
+ struct ip_conntrack_expect *exp);
+#endif /* __KERNEL__ */
+#endif /* _CONNTRACK_PPTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
@@ -0,0 +1,114 @@
+#ifndef _CONNTRACK_PROTO_GRE_H
+#define _CONNTRACK_PROTO_GRE_H
+#include <asm/byteorder.h>
+
+/* GRE PROTOCOL HEADER */
+
+/* GRE Version field */
+#define GRE_VERSION_1701 0x0
+#define GRE_VERSION_PPTP 0x1
+
+/* GRE Protocol field */
+#define GRE_PROTOCOL_PPTP 0x880B
+
+/* GRE Flags */
+#define GRE_FLAG_C 0x80
+#define GRE_FLAG_R 0x40
+#define GRE_FLAG_K 0x20
+#define GRE_FLAG_S 0x10
+#define GRE_FLAG_A 0x80
+
+#define GRE_IS_C(f) ((f)&GRE_FLAG_C)
+#define GRE_IS_R(f) ((f)&GRE_FLAG_R)
+#define GRE_IS_K(f) ((f)&GRE_FLAG_K)
+#define GRE_IS_S(f) ((f)&GRE_FLAG_S)
+#define GRE_IS_A(f) ((f)&GRE_FLAG_A)
+
+/* GRE is a mess: Four different standards */
+struct gre_hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u16 rec:3,
+ srr:1,
+ seq:1,
+ key:1,
+ routing:1,
+ csum:1,
+ version:3,
+ reserved:4,
+ ack:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u16 csum:1,
+ routing:1,
+ key:1,
+ seq:1,
+ srr:1,
+ rec:3,
+ ack:1,
+ reserved:4,
+ version:3;
+#else
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+ __u16 protocol;
+};
+
+/* modified GRE header for PPTP */
+struct gre_hdr_pptp {
+ __u8 flags; /* bitfield */
+ __u8 version; /* should be GRE_VERSION_PPTP */
+ __u16 protocol; /* should be GRE_PROTOCOL_PPTP */
+ __u16 payload_len; /* size of ppp payload, not inc. gre header */
+ __u16 call_id; /* peer's call_id for this session */
+ __u32 seq; /* sequence number. Present if S==1 */
+ __u32 ack; /* seq number of highest packet recieved by */
+ /* sender in this session */
+};
+
+
+/* this is part of ip_conntrack */
+struct ip_ct_gre {
+ unsigned int stream_timeout;
+ unsigned int timeout;
+};
+
+#ifdef __KERNEL__
+struct ip_conntrack_expect;
+struct ip_conntrack;
+
+/* structure for original <-> reply keymap */
+struct ip_ct_gre_keymap {
+ struct list_head list;
+
+ struct ip_conntrack_tuple tuple;
+};
+
+/* add new tuple->key_reply pair to keymap */
+int ip_ct_gre_keymap_add(struct ip_conntrack *ct,
+ struct ip_conntrack_tuple *t,
+ int reply);
+
+/* delete keymap entries */
+void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct);
+
+
+/* get pointer to gre key, if present */
+static inline u_int32_t *gre_key(struct gre_hdr *greh)
+{
+ if (!greh->key)
+ return NULL;
+ if (greh->csum || greh->routing)
+ return (u_int32_t *) (greh+sizeof(*greh)+4);
+ return (u_int32_t *) (greh+sizeof(*greh));
+}
+
+/* get pointer ot gre csum, if present */
+static inline u_int16_t *gre_csum(struct gre_hdr *greh)
+{
+ if (!greh->csum)
+ return NULL;
+ return (u_int16_t *) (greh+sizeof(*greh));
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _CONNTRACK_PROTO_GRE_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
--- a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
+++ b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
@@ -28,6 +28,9 @@ union ip_conntrack_manip_proto
struct {
u_int16_t port;
} sctp;
+ struct {
+ u_int16_t key; /* key is 32bit, pptp only uses 16 */
+ } gre;
};
/* The manipulable part of the tuple. */
@@ -61,6 +64,10 @@ struct ip_conntrack_tuple
struct {
u_int16_t port;
} sctp;
+ struct {
+ u_int16_t key; /* key is 32bit,
+ * pptp only uses 16 */
+ } gre;
} u;
/* The protocol. */
diff --git a/include/linux/netfilter_ipv4/ip_nat_pptp.h b/include/linux/netfilter_ipv4/ip_nat_pptp.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat_pptp.h
@@ -0,0 +1,11 @@
+/* PPTP constants and structs */
+#ifndef _NAT_PPTP_H
+#define _NAT_PPTP_H
+
+/* conntrack private data */
+struct ip_nat_pptp {
+ u_int16_t pns_call_id; /* NAT'ed PNS call id */
+ u_int16_t pac_call_id; /* NAT'ed PAC call id */
+};
+
+#endif /* _NAT_PPTP_H */
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -129,6 +129,22 @@ config IP_NF_AMANDA
To compile it as a module, choose M here. If unsure, say Y.
+config IP_NF_PPTP
+ tristate 'PPTP protocol support'
+ help
+ This module adds support for PPTP (Point to Point Tunnelling
+ Protocol, RFC2637) conncection tracking and NAT.
+
+ If you are running PPTP sessions over a stateful firewall or NAT
+ box, you may want to enable this feature.
+
+ Please note that not all PPTP modes of operation are supported yet.
+ For more info, read top of the file
+ net/ipv4/netfilter/ip_conntrack_pptp.c
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
config IP_NF_QUEUE
tristate "IP Userspace queueing via NETLINK (OBSOLETE)"
help
@@ -613,6 +629,12 @@ config IP_NF_NAT_AMANDA
default IP_NF_NAT if IP_NF_AMANDA=y
default m if IP_NF_AMANDA=m
+config IP_NF_NAT_PPTP
+ tristate
+ depends on IP_NF_NAT!=n && IP_NF_PPTP!=n
+ default IP_NF_NAT if IP_NF_PPTP=y
+ default m if IP_NF_PPTP=m
+
# mangle + specific targets
config IP_NF_MANGLE
tristate "Packet mangling"
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -6,6 +6,9 @@
ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
+ip_conntrack_pptp-objs := ip_conntrack_helper_pptp.o ip_conntrack_proto_gre.o
+ip_nat_pptp-objs := ip_nat_helper_pptp.o ip_nat_proto_gre.o
+
# connection tracking
obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
@@ -17,6 +20,7 @@ obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) +=
obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o
# connection tracking helpers
+obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o
obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o
obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o
obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
@@ -24,6 +28,7 @@ obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_
obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o
# NAT helpers
+obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o
obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o
obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o
obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
diff --git a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c
@@ -0,0 +1,801 @@
+/*
+ * ip_conntrack_pptp.c - Version 3.0
+ *
+ * Connection tracking support for PPTP (Point to Point Tunneling Protocol).
+ * PPTP is a a protocol for creating virtual private networks.
+ * It is a specification defined by Microsoft and some vendors
+ * working with Microsoft. PPTP is built on top of a modified
+ * version of the Internet Generic Routing Encapsulation Protocol.
+ * GRE is defined in RFC 1701 and RFC 1702. Documentation of
+ * PPTP can be found in RFC 2637
+ *
+ * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ *
+ * Limitations:
+ * - We blindly assume that control connections are always
+ * established in PNS->PAC direction. This is a violation
+ * of RFFC2673
+ * - We can only support one single call within each session
+ *
+ * TODO:
+ * - testing of incoming PPTP calls
+ *
+ * Changes:
+ * 2002-02-05 - Version 1.3
+ * - Call ip_conntrack_unexpect_related() from
+ * pptp_timeout_related() to destroy expectations in case
+ * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
+ * (Philip Craig <philipc@snapgear.com>)
+ * - Add Version information at module loadtime
+ * 2002-02-10 - Version 1.6
+ * - move to C99 style initializers
+ * - remove second expectation if first arrives
+ * 2004-10-22 - Version 2.0
+ * - merge Mandrake's 2.6.x port with recent 2.6.x API changes
+ * - fix lots of linear skb assumptions from Mandrake's port
+ * 2005-06-10 - Version 2.1
+ * - use ip_conntrack_expect_free() instead of kfree() on the
+ * expect's (which are from the slab for quite some time)
+ * 2005-06-10 - Version 3.0
+ * - port helper to post-2.6.11 API changes,
+ * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/)
+ * 2005-07-30 - Version 3.1
+ * - port helper to 2.6.13 API changes
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
+
+#define IP_CT_PPTP_VERSION "3.1"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
+MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
+
+static DEFINE_SPINLOCK(ip_pptp_lock);
+
+int
+(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct PptpControlHeader *ctlh,
+ union pptp_ctrl_union *pptpReq);
+
+int
+(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct PptpControlHeader *ctlh,
+ union pptp_ctrl_union *pptpReq);
+
+int
+(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig,
+ struct ip_conntrack_expect *expect_reply);
+
+void
+(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct,
+ struct ip_conntrack_expect *exp);
+
+#if 0
+#include "ip_conntrack_pptp_priv.h"
+#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define SECS *HZ
+#define MINS * 60 SECS
+#define HOURS * 60 MINS
+
+#define PPTP_GRE_TIMEOUT (10 MINS)
+#define PPTP_GRE_STREAM_TIMEOUT (5 HOURS)
+
+static void pptp_expectfn(struct ip_conntrack *ct,
+ struct ip_conntrack_expect *exp)
+{
+ DEBUGP("increasing timeouts\n");
+
+ /* increase timeout of GRE data channel conntrack entry */
+ ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
+ ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
+
+ /* Can you see how rusty this code is, compared with the pre-2.6.11
+ * one? That's what happened to my shiny newnat of 2002 ;( -HW */
+
+ if (!ip_nat_pptp_hook_expectfn) {
+ struct ip_conntrack_tuple inv_t;
+ struct ip_conntrack_expect *exp_other;
+
+ /* obviously this tuple inversion only works until you do NAT */
+ invert_tuplepr(&inv_t, &exp->tuple);
+ DEBUGP("trying to unexpect other dir: ");
+ DUMP_TUPLE(&inv_t);
+
+ exp_other = __ip_conntrack_expect_find(&inv_t);
+ if (exp_other) {
+ /* delete other expectation. */
+ DEBUGP("found\n");
+ ip_conntrack_unexpect_related(exp_other);
+ } else {
+ DEBUGP("not found\n");
+ }
+ } else {
+ /* we need more than simple inversion */
+ ip_nat_pptp_hook_expectfn(ct, exp);
+ }
+}
+
+static int timeout_ct_or_exp(const struct ip_conntrack_tuple *t)
+{
+ struct ip_conntrack_tuple_hash *h;
+ struct ip_conntrack_expect *exp;
+
+ DEBUGP("trying to timeout ct or exp for tuple ");
+ DUMP_TUPLE(t);
+
+ h = __ip_conntrack_find(t, NULL);
+ if (h) {
+ struct ip_conntrack *sibling = tuplehash_to_ctrack(h);
+ DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
+ sibling->proto.gre.timeout = 0;
+ sibling->proto.gre.stream_timeout = 0;
+ /* refresh_acct will not modify counters if skb == NULL */
+ ip_ct_refresh_acct(sibling, 0, NULL, 0);
+ return 1;
+ } else {
+ exp = __ip_conntrack_expect_find(t);
+ if (exp) {
+ DEBUGP("unexpect_related of expect %p\n", exp);
+ ip_conntrack_unexpect_related(exp);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* timeout GRE data connections */
+static int pptp_timeout_related(struct ip_conntrack *ct)
+{
+ struct ip_conntrack_tuple t;
+ int ret;
+
+ /* Since ct->sibling_list has literally rusted away in 2.6.11,
+ * we now need another way to find out about our sibling
+ * contrack and expects... -HW */
+
+ /* try original (pns->pac) tuple */
+ memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
+ t.dst.protonum = IPPROTO_GRE;
+ t.src.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id);
+ t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id);
+
+ ret = timeout_ct_or_exp(&t);
+
+ /* try reply (pac->pns) tuple */
+ memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
+ t.dst.protonum = IPPROTO_GRE;
+ t.src.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id);
+ t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id);
+
+ ret += timeout_ct_or_exp(&t);
+
+ return ret;
+}
+
+/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
+static inline int
+exp_gre(struct ip_conntrack *master,
+ u_int32_t seq,
+ u_int16_t callid,
+ u_int16_t peer_callid)
+{
+ struct ip_conntrack_tuple inv_tuple;
+ struct ip_conntrack_tuple exp_tuples[] = {
+ /* tuple in original direction, PNS->PAC */
+ { .src = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
+ .u = { .gre = { .key = peer_callid } }
+ },
+ .dst = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
+ .u = { .gre = { .key = callid } },
+ .protonum = IPPROTO_GRE
+ },
+ },
+ /* tuple in reply direction, PAC->PNS */
+ { .src = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip,
+ .u = { .gre = { .key = callid } }
+ },
+ .dst = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip,
+ .u = { .gre = { .key = peer_callid } },
+ .protonum = IPPROTO_GRE
+ },
+ }
+ };
+
+ struct ip_conntrack_expect *exp_orig, *exp_reply;
+
+ exp_orig = ip_conntrack_expect_alloc(master);
+ if (exp_orig == NULL)
+ return 1;
+
+ exp_reply = ip_conntrack_expect_alloc(master);
+ if (exp_reply == NULL) {
+ ip_conntrack_expect_put(exp_orig);
+ return 1;
+ }
+
+ memcpy(&exp_orig->tuple, &exp_tuples[0], sizeof(exp_orig->tuple));
+
+ exp_orig->mask.src.ip = 0xffffffff;
+ exp_orig->mask.src.u.all = 0;
+ exp_orig->mask.dst.u.all = 0;
+ exp_orig->mask.dst.u.gre.key = 0xffff;
+ exp_orig->mask.dst.ip = 0xffffffff;
+ exp_orig->mask.dst.protonum = 0xff;
+
+ exp_orig->master = master;
+ exp_orig->expectfn = pptp_expectfn;
+
+ exp_orig->dir = IP_CT_DIR_ORIGINAL;
+
+ /* both expectations are identical apart from tuple */
+ memcpy(exp_reply, exp_orig, sizeof(*exp_reply));
+ memcpy(&exp_reply->tuple, &exp_tuples[1], sizeof(exp_reply->tuple));
+
+ exp_reply->dir = !exp_orig->dir;
+
+ if (ip_nat_pptp_hook_exp_gre)
+ return ip_nat_pptp_hook_exp_gre(exp_orig, exp_reply);
+ else {
+
+ DEBUGP("calling expect_related PNS->PAC");
+ DUMP_TUPLE(&exp_orig->tuple);
+
+ if (ip_conntrack_expect_related(exp_orig) != 0) {
+ ip_conntrack_expect_put(exp_orig);
+ ip_conntrack_expect_put(exp_reply);
+ DEBUGP("cannot expect_related()\n");
+ return 1;
+ }
+
+ DEBUGP("calling expect_related PAC->PNS");
+ DUMP_TUPLE(&exp_reply->tuple);
+
+ if (ip_conntrack_expect_related(exp_reply) != 0) {
+ ip_conntrack_unexpect_related(exp_orig);
+ ip_conntrack_expect_put(exp_reply);
+ DEBUGP("cannot expect_related()\n");
+ return 1;
+ }
+
+ /* Add GRE keymap entries */
+ if (ip_ct_gre_keymap_add(master, &exp_reply->tuple, 0) != 0) {
+ ip_conntrack_unexpect_related(exp_orig);
+ ip_conntrack_unexpect_related(exp_reply);
+ DEBUGP("cannot keymap_add() exp\n");
+ return 1;
+ }
+
+ invert_tuplepr(&inv_tuple, &exp_reply->tuple);
+ if (ip_ct_gre_keymap_add(master, &inv_tuple, 1) != 0) {
+ ip_conntrack_unexpect_related(exp_orig);
+ ip_conntrack_unexpect_related(exp_reply);
+ ip_ct_gre_keymap_destroy(master);
+ DEBUGP("cannot keymap_add() exp_inv\n");
+ return 1;
+ }
+
+ }
+
+ ip_conntrack_expect_put(exp_orig);
+ ip_conntrack_expect_put(exp_reply);
+ return 0;
+}
+
+static inline int
+pptp_inbound_pkt(struct sk_buff **pskb,
+ struct tcphdr *tcph,
+ unsigned int ctlhoff,
+ size_t datalen,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo)
+{
+ struct PptpControlHeader _ctlh, *ctlh;
+ unsigned int reqlen;
+ union pptp_ctrl_union _pptpReq, *pptpReq;
+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
+ u_int16_t msg, *cid, *pcid;
+ u_int32_t seq;
+
+ ctlh = skb_header_pointer(*pskb, ctlhoff, sizeof(_ctlh), &_ctlh);
+ if (unlikely(!ctlh)) {
+ DEBUGP("error during skb_header_pointer\n");
+ return NF_ACCEPT;
+ }
+
+ reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh);
+ pptpReq = skb_header_pointer(*pskb, ctlhoff+sizeof(_ctlh),
+ reqlen, &_pptpReq);
+ if (unlikely(!pptpReq)) {
+ DEBUGP("error during skb_header_pointer\n");
+ return NF_ACCEPT;
+ }
+
+ msg = ntohs(ctlh->messageType);
+ DEBUGP("inbound control message %s\n", strMName[msg]);
+
+ switch (msg) {
+ case PPTP_START_SESSION_REPLY:
+ if (reqlen < sizeof(_pptpReq.srep)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* server confirms new control session */
+ if (info->sstate < PPTP_SESSION_REQUESTED) {
+ DEBUGP("%s without START_SESS_REQUEST\n",
+ strMName[msg]);
+ break;
+ }
+ if (pptpReq->srep.resultCode == PPTP_START_OK)
+ info->sstate = PPTP_SESSION_CONFIRMED;
+ else
+ info->sstate = PPTP_SESSION_ERROR;
+ break;
+
+ case PPTP_STOP_SESSION_REPLY:
+ if (reqlen < sizeof(_pptpReq.strep)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* server confirms end of control session */
+ if (info->sstate > PPTP_SESSION_STOPREQ) {
+ DEBUGP("%s without STOP_SESS_REQUEST\n",
+ strMName[msg]);
+ break;
+ }
+ if (pptpReq->strep.resultCode == PPTP_STOP_OK)
+ info->sstate = PPTP_SESSION_NONE;
+ else
+ info->sstate = PPTP_SESSION_ERROR;
+ break;
+
+ case PPTP_OUT_CALL_REPLY:
+ if (reqlen < sizeof(_pptpReq.ocack)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* server accepted call, we now expect GRE frames */
+ if (info->sstate != PPTP_SESSION_CONFIRMED) {
+ DEBUGP("%s but no session\n", strMName[msg]);
+ break;
+ }
+ if (info->cstate != PPTP_CALL_OUT_REQ &&
+ info->cstate != PPTP_CALL_OUT_CONF) {
+ DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]);
+ break;
+ }
+ if (pptpReq->ocack.resultCode != PPTP_OUTCALL_CONNECT) {
+ info->cstate = PPTP_CALL_NONE;
+ break;
+ }
+
+ cid = &pptpReq->ocack.callID;
+ pcid = &pptpReq->ocack.peersCallID;
+
+ info->pac_call_id = ntohs(*cid);
+
+ if (htons(info->pns_call_id) != *pcid) {
+ DEBUGP("%s for unknown callid %u\n",
+ strMName[msg], ntohs(*pcid));
+ break;
+ }
+
+ DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg],
+ ntohs(*cid), ntohs(*pcid));
+
+ info->cstate = PPTP_CALL_OUT_CONF;
+
+ seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
+ + sizeof(struct PptpControlHeader)
+ + ((void *)pcid - (void *)pptpReq);
+
+ if (exp_gre(ct, seq, *cid, *pcid) != 0)
+ printk("ip_conntrack_pptp: error during exp_gre\n");
+ break;
+
+ case PPTP_IN_CALL_REQUEST:
+ if (reqlen < sizeof(_pptpReq.icack)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* server tells us about incoming call request */
+ if (info->sstate != PPTP_SESSION_CONFIRMED) {
+ DEBUGP("%s but no session\n", strMName[msg]);
+ break;
+ }
+ pcid = &pptpReq->icack.peersCallID;
+ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
+ info->cstate = PPTP_CALL_IN_REQ;
+ info->pac_call_id = ntohs(*pcid);
+ break;
+
+ case PPTP_IN_CALL_CONNECT:
+ if (reqlen < sizeof(_pptpReq.iccon)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* server tells us about incoming call established */
+ if (info->sstate != PPTP_SESSION_CONFIRMED) {
+ DEBUGP("%s but no session\n", strMName[msg]);
+ break;
+ }
+ if (info->sstate != PPTP_CALL_IN_REP
+ && info->sstate != PPTP_CALL_IN_CONF) {
+ DEBUGP("%s but never sent IN_CALL_REPLY\n",
+ strMName[msg]);
+ break;
+ }
+
+ pcid = &pptpReq->iccon.peersCallID;
+ cid = &info->pac_call_id;
+
+ if (info->pns_call_id != ntohs(*pcid)) {
+ DEBUGP("%s for unknown CallID %u\n",
+ strMName[msg], ntohs(*cid));
+ break;
+ }
+
+ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
+ info->cstate = PPTP_CALL_IN_CONF;
+
+ /* we expect a GRE connection from PAC to PNS */
+ seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
+ + sizeof(struct PptpControlHeader)
+ + ((void *)pcid - (void *)pptpReq);
+
+ if (exp_gre(ct, seq, *cid, *pcid) != 0)
+ printk("ip_conntrack_pptp: error during exp_gre\n");
+
+ break;
+
+ case PPTP_CALL_DISCONNECT_NOTIFY:
+ if (reqlen < sizeof(_pptpReq.disc)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* server confirms disconnect */
+ cid = &pptpReq->disc.callID;
+ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
+ info->cstate = PPTP_CALL_NONE;
+
+ /* untrack this call id, unexpect GRE packets */
+ pptp_timeout_related(ct);
+ break;
+
+ case PPTP_WAN_ERROR_NOTIFY:
+ break;
+
+ case PPTP_ECHO_REQUEST:
+ case PPTP_ECHO_REPLY:
+ /* I don't have to explain these ;) */
+ break;
+ default:
+ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)
+ ? strMName[msg]:strMName[0], msg);
+ break;
+ }
+
+
+ if (ip_nat_pptp_hook_inbound)
+ return ip_nat_pptp_hook_inbound(pskb, ct, ctinfo, ctlh,
+ pptpReq);
+
+ return NF_ACCEPT;
+
+}
+
+static inline int
+pptp_outbound_pkt(struct sk_buff **pskb,
+ struct tcphdr *tcph,
+ unsigned int ctlhoff,
+ size_t datalen,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo)
+{
+ struct PptpControlHeader _ctlh, *ctlh;
+ unsigned int reqlen;
+ union pptp_ctrl_union _pptpReq, *pptpReq;
+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
+ u_int16_t msg, *cid, *pcid;
+
+ ctlh = skb_header_pointer(*pskb, ctlhoff, sizeof(_ctlh), &_ctlh);
+ if (!ctlh)
+ return NF_ACCEPT;
+
+ reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh);
+ pptpReq = skb_header_pointer(*pskb, ctlhoff+sizeof(_ctlh), reqlen,
+ &_pptpReq);
+ if (!pptpReq)
+ return NF_ACCEPT;
+
+ msg = ntohs(ctlh->messageType);
+ DEBUGP("outbound control message %s\n", strMName[msg]);
+
+ switch (msg) {
+ case PPTP_START_SESSION_REQUEST:
+ /* client requests for new control session */
+ if (info->sstate != PPTP_SESSION_NONE) {
+ DEBUGP("%s but we already have one",
+ strMName[msg]);
+ }
+ info->sstate = PPTP_SESSION_REQUESTED;
+ break;
+ case PPTP_STOP_SESSION_REQUEST:
+ /* client requests end of control session */
+ info->sstate = PPTP_SESSION_STOPREQ;
+ break;
+
+ case PPTP_OUT_CALL_REQUEST:
+ if (reqlen < sizeof(_pptpReq.ocreq)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ /* FIXME: break; */
+ }
+
+ /* client initiating connection to server */
+ if (info->sstate != PPTP_SESSION_CONFIRMED) {
+ DEBUGP("%s but no session\n",
+ strMName[msg]);
+ break;
+ }
+ info->cstate = PPTP_CALL_OUT_REQ;
+ /* track PNS call id */
+ cid = &pptpReq->ocreq.callID;
+ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
+ info->pns_call_id = ntohs(*cid);
+ break;
+ case PPTP_IN_CALL_REPLY:
+ if (reqlen < sizeof(_pptpReq.icack)) {
+ DEBUGP("%s: short packet\n", strMName[msg]);
+ break;
+ }
+
+ /* client answers incoming call */
+ if (info->cstate != PPTP_CALL_IN_REQ
+ && info->cstate != PPTP_CALL_IN_REP) {
+ DEBUGP("%s without incall_req\n",
+ strMName[msg]);
+ break;
+ }
+ if (pptpReq->icack.resultCode != PPTP_INCALL_ACCEPT) {
+ info->cstate = PPTP_CALL_NONE;
+ break;
+ }
+ pcid = &pptpReq->icack.peersCallID;
+ if (info->pac_call_id != ntohs(*pcid)) {
+ DEBUGP("%s for unknown call %u\n",
+ strMName[msg], ntohs(*pcid));
+ break;
+ }
+ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid));
+ /* part two of the three-way handshake */
+ info->cstate = PPTP_CALL_IN_REP;
+ info->pns_call_id = ntohs(pptpReq->icack.callID);
+ break;
+
+ case PPTP_CALL_CLEAR_REQUEST:
+ /* client requests hangup of call */
+ if (info->sstate != PPTP_SESSION_CONFIRMED) {
+ DEBUGP("CLEAR_CALL but no session\n");
+ break;
+ }
+ /* FUTURE: iterate over all calls and check if
+ * call ID is valid. We don't do this without newnat,
+ * because we only know about last call */
+ info->cstate = PPTP_CALL_CLEAR_REQ;
+ break;
+ case PPTP_SET_LINK_INFO:
+ break;
+ case PPTP_ECHO_REQUEST:
+ case PPTP_ECHO_REPLY:
+ /* I don't have to explain these ;) */
+ break;
+ default:
+ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)?
+ strMName[msg]:strMName[0], msg);
+ /* unknown: no need to create GRE masq table entry */
+ break;
+ }
+
+ if (ip_nat_pptp_hook_outbound)
+ return ip_nat_pptp_hook_outbound(pskb, ct, ctinfo, ctlh,
+ pptpReq);
+
+ return NF_ACCEPT;
+}
+
+
+/* track caller id inside control connection, call expect_related */
+static int
+conntrack_pptp_help(struct sk_buff **pskb,
+ struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
+
+{
+ struct pptp_pkt_hdr _pptph, *pptph;
+
+ struct tcphdr _tcph, *tcph;
+ u_int32_t tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
+ u_int32_t datalen;
+ void *datalimit;
+ int dir = CTINFO2DIR(ctinfo);
+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
+ unsigned int nexthdr_off;
+
+ int oldsstate, oldcstate;
+ int ret;
+
+ /* don't do any tracking before tcp handshake complete */
+ if (ctinfo != IP_CT_ESTABLISHED
+ && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
+ DEBUGP("ctinfo = %u, skipping\n", ctinfo);
+ return NF_ACCEPT;
+ }
+
+ nexthdr_off = (*pskb)->nh.iph->ihl*4;
+ tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph),
+ &_tcph);
+ if (!tcph)
+ return NF_ACCEPT;
+
+ /* not a complete TCP header? */
+ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
+ DEBUGP("tcplen = %u\n", tcplen);
+ return NF_ACCEPT;
+ }
+
+
+ datalen = tcplen - tcph->doff * 4;
+
+ /* checksum invalid? */
+ if (tcp_v4_check(tcph, tcplen, (*pskb)->nh.iph->saddr,
+ (*pskb)->nh.iph->daddr,
+ csum_partial((char *) tcph, tcplen, 0))) {
+ DEBUGP(" bad csum\n");
+ /* W2K PPTP server sends TCP packets with wrong checksum :(( */
+ /* return NF_ACCEPT */
+ }
+
+ if (tcph->fin || tcph->rst) {
+ DEBUGP("RST/FIN received, timeouting GRE\n");
+ /* can't do this after real newnat */
+ info->cstate = PPTP_CALL_NONE;
+
+ /* untrack this call id, unexpect GRE packets */
+ pptp_timeout_related(ct);
+ }
+
+ nexthdr_off += tcph->doff*4;
+ pptph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4 + tcph->doff*4,
+ sizeof(_pptph), &_pptph);
+ if (!pptph) {
+ DEBUGP("no full PPTP header, can't track\n");
+ return NF_ACCEPT;
+ }
+
+ datalimit = (void *) pptph + datalen;
+
+ /* if it's not a control message we can't do anything with it */
+ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
+ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
+ DEBUGP("not a control packet\n");
+ return NF_ACCEPT;
+ }
+
+ oldsstate = info->sstate;
+ oldcstate = info->cstate;
+
+ spin_lock_bh(&ip_pptp_lock);
+
+ nexthdr_off += sizeof(_pptph);
+ /* FIXME: We just blindly assume that the control connection is always
+ * established from PNS->PAC. However, RFC makes no guarantee */
+ if (dir == IP_CT_DIR_ORIGINAL)
+ /* client -> server (PNS -> PAC) */
+ ret = pptp_outbound_pkt(pskb, tcph, nexthdr_off, datalen, ct,
+ ctinfo);
+ else
+ /* server -> client (PAC -> PNS) */
+ ret = pptp_inbound_pkt(pskb, tcph, nexthdr_off, datalen, ct,
+ ctinfo);
+ DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
+ oldsstate, info->sstate, oldcstate, info->cstate);
+ spin_unlock_bh(&ip_pptp_lock);
+
+ return ret;
+}
+
+/* control protocol helper */
+static struct ip_conntrack_helper pptp = {
+ .list = { NULL, NULL },
+ .name = "pptp",
+ .me = THIS_MODULE,
+ .max_expected = 2,
+ .timeout = 5 * 60,
+ .tuple = { .src = { .ip = 0,
+ .u = { .tcp = { .port =
+ __constant_htons(PPTP_CONTROL_PORT) } }
+ },
+ .dst = { .ip = 0,
+ .u = { .all = 0 },
+ .protonum = IPPROTO_TCP
+ }
+ },
+ .mask = { .src = { .ip = 0,
+ .u = { .tcp = { .port = 0xffff } }
+ },
+ .dst = { .ip = 0,
+ .u = { .all = 0 },
+ .protonum = 0xff
+ }
+ },
+ .help = conntrack_pptp_help
+};
+
+extern void __exit ip_ct_proto_gre_fini(void);
+extern int __init ip_ct_proto_gre_init(void);
+
+/* ip_conntrack_pptp initialization */
+static int __init init(void)
+{
+ int retcode;
+
+ retcode = ip_ct_proto_gre_init();
+ if (retcode < 0)
+ return retcode;
+
+ DEBUGP(" registering helper\n");
+ if ((retcode = ip_conntrack_helper_register(&pptp))) {
+ printk(KERN_ERR "Unable to register conntrack application "
+ "helper for pptp: %d\n", retcode);
+ ip_ct_proto_gre_fini();
+ return -EIO;
+ }
+
+ printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ ip_conntrack_helper_unregister(&pptp);
+ ip_ct_proto_gre_fini();
+ printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(ip_pptp_lock);
+EXPORT_SYMBOL(ip_nat_pptp_hook_outbound);
+EXPORT_SYMBOL(ip_nat_pptp_hook_inbound);
+EXPORT_SYMBOL(ip_nat_pptp_hook_exp_gre);
+EXPORT_SYMBOL(ip_nat_pptp_hook_expectfn);
diff --git a/net/ipv4/netfilter/ip_conntrack_pptp_priv.h b/net/ipv4/netfilter/ip_conntrack_pptp_priv.h
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_pptp_priv.h
@@ -0,0 +1,24 @@
+#ifndef _IP_CT_PPTP_PRIV_H
+#define _IP_CT_PPTP_PRIV_H
+
+/* PptpControlMessageType names */
+static const char *strMName[] = {
+ "UNKNOWN_MESSAGE",
+ "START_SESSION_REQUEST",
+ "START_SESSION_REPLY",
+ "STOP_SESSION_REQUEST",
+ "STOP_SESSION_REPLY",
+ "ECHO_REQUEST",
+ "ECHO_REPLY",
+ "OUT_CALL_REQUEST",
+ "OUT_CALL_REPLY",
+ "IN_CALL_REQUEST",
+ "IN_CALL_REPLY",
+ "IN_CALL_CONNECT",
+ "CALL_CLEAR_REQUEST",
+ "CALL_DISCONNECT_NOTIFY",
+ "WAN_ERROR_NOTIFY",
+ "SET_LINK_INFO"
+};
+
+#endif
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_proto_gre.c
@@ -0,0 +1,354 @@
+/*
+ * ip_conntrack_proto_gre.c - Version 3.0
+ *
+ * Connection tracking protocol helper module for GRE.
+ *
+ * GRE is a generic encapsulation protocol, which is generally not very
+ * suited for NAT, as it has no protocol-specific part as port numbers.
+ *
+ * It has an optional key field, which may help us distinguishing two
+ * connections between the same two hosts.
+ *
+ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
+ *
+ * PPTP is built on top of a modified version of GRE, and has a mandatory
+ * field called "CallID", which serves us for the same purpose as the key
+ * field in plain GRE.
+ *
+ * Documentation about PPTP can be found in RFC 2637
+ *
+ * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/list.h>
+
+static DEFINE_RWLOCK(ip_ct_gre_lock);
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+
+#include <linux/netfilter_ipv4/listhelp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+
+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
+MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
+
+/* shamelessly stolen from ip_conntrack_proto_udp.c */
+#define GRE_TIMEOUT (30*HZ)
+#define GRE_STREAM_TIMEOUT (180*HZ)
+
+#if 0
+#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
+#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \
+ NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \
+ NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key))
+#else
+#define DEBUGP(x, args...)
+#define DUMP_TUPLE_GRE(x)
+#endif
+
+/* GRE KEYMAP HANDLING FUNCTIONS */
+static LIST_HEAD(gre_keymap_list);
+
+static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
+ const struct ip_conntrack_tuple *t)
+{
+ return ((km->tuple.src.ip == t->src.ip) &&
+ (km->tuple.dst.ip == t->dst.ip) &&
+ (km->tuple.dst.protonum == t->dst.protonum) &&
+ (km->tuple.dst.u.all == t->dst.u.all));
+}
+
+/* look up the source key for a given tuple */
+static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
+{
+ struct ip_ct_gre_keymap *km;
+ u_int32_t key = 0;
+
+ read_lock_bh(&ip_ct_gre_lock);
+ km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
+ struct ip_ct_gre_keymap *, t);
+ if (km)
+ key = km->tuple.src.u.gre.key;
+ read_unlock_bh(&ip_ct_gre_lock);
+
+ DEBUGP("lookup src key 0x%x up key for ", key);
+ DUMP_TUPLE_GRE(t);
+
+ return key;
+}
+
+/* add a single keymap entry, associate with specified master ct */
+int
+ip_ct_gre_keymap_add(struct ip_conntrack *ct,
+ struct ip_conntrack_tuple *t, int reply)
+{
+ struct ip_ct_gre_keymap *km, *old;
+
+ if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
+ DEBUGP("refusing to add GRE keymap to non-pptp session\n");
+ return -1;
+ }
+
+ km = kmalloc(sizeof(*km), GFP_ATOMIC);
+ if (!km)
+ return -1;
+
+ /* initializing list head should be sufficient */
+ memset(km, 0, sizeof(*km));
+
+ memcpy(&km->tuple, t, sizeof(*t));
+
+ if (!reply) {
+ if (ct->help.ct_pptp_info.keymap_orig) {
+ kfree(km);
+
+ /* check whether it's a retransmission */
+ old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
+ struct ip_ct_gre_keymap *, t);
+ if (old == ct->help.ct_pptp_info.keymap_orig) {
+ DEBUGP("retransmission\n");
+ return 0;
+ }
+
+ DEBUGP("trying to override keymap_orig for ct %p\n",
+ ct);
+ return -2;
+ }
+ ct->help.ct_pptp_info.keymap_orig = km;
+ } else {
+ if (ct->help.ct_pptp_info.keymap_reply) {
+ kfree(km);
+
+ /* check whether it's a retransmission */
+ old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
+ struct ip_ct_gre_keymap *, t);
+ if (old == ct->help.ct_pptp_info.keymap_reply) {
+ DEBUGP("retransmission\n");
+ return 0;
+ }
+
+ DEBUGP("trying to override keymap_reply for ct %p\n",
+ ct);
+ return -2;
+ }
+ ct->help.ct_pptp_info.keymap_reply = km;
+ }
+
+ DEBUGP("adding new entry %p: ", km);
+ DUMP_TUPLE_GRE(&km->tuple);
+
+ write_lock_bh(&ip_ct_gre_lock);
+ list_append(&gre_keymap_list, km);
+ write_unlock_bh(&ip_ct_gre_lock);
+
+ return 0;
+}
+
+/* destroy the keymap entries associated with specified master ct */
+void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct)
+{
+ DEBUGP("entering for ct %p\n", ct);
+
+ if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
+ DEBUGP("refusing to destroy GRE keymap to non-pptp session\n");
+ return;
+ }
+
+ write_lock_bh(&ip_ct_gre_lock);
+ if (ct->help.ct_pptp_info.keymap_orig) {
+ DEBUGP("removing %p from list\n",
+ ct->help.ct_pptp_info.keymap_orig);
+ list_del(&ct->help.ct_pptp_info.keymap_orig->list);
+ kfree(ct->help.ct_pptp_info.keymap_orig);
+ ct->help.ct_pptp_info.keymap_orig = NULL;
+ }
+ if (ct->help.ct_pptp_info.keymap_reply) {
+ DEBUGP("removing %p from list\n",
+ ct->help.ct_pptp_info.keymap_reply);
+ list_del(&ct->help.ct_pptp_info.keymap_reply->list);
+ kfree(ct->help.ct_pptp_info.keymap_reply);
+ ct->help.ct_pptp_info.keymap_reply = NULL;
+ }
+ write_unlock_bh(&ip_ct_gre_lock);
+}
+
+
+/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
+
+/* invert gre part of tuple */
+static int gre_invert_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *orig)
+{
+ tuple->dst.u.gre.key = orig->src.u.gre.key;
+ tuple->src.u.gre.key = orig->dst.u.gre.key;
+
+ return 1;
+}
+
+/* gre hdr info to tuple */
+static int gre_pkt_to_tuple(const struct sk_buff *skb,
+ unsigned int dataoff,
+ struct ip_conntrack_tuple *tuple)
+{
+ struct gre_hdr_pptp _pgrehdr, *pgrehdr;
+ u_int32_t srckey;
+ struct gre_hdr _grehdr, *grehdr;
+
+ /* first only delinearize old RFC1701 GRE header */
+ grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
+ if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
+ /* try to behave like "ip_conntrack_proto_generic" */
+ tuple->src.u.all = 0;
+ tuple->dst.u.all = 0;
+ return 1;
+ }
+
+ /* PPTP header is variable length, only need up to the call_id field */
+ pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
+ if (!pgrehdr)
+ return 1;
+
+ if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
+ DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
+ return 0;
+ }
+
+ tuple->dst.u.gre.key = pgrehdr->call_id;
+ srckey = gre_keymap_lookup(tuple);
+ tuple->src.u.gre.key = srckey;
+
+ return 1;
+}
+
+/* print gre part of tuple */
+static int gre_print_tuple(struct seq_file *s,
+ const struct ip_conntrack_tuple *tuple)
+{
+ return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
+ ntohs(tuple->src.u.gre.key),
+ ntohs(tuple->dst.u.gre.key));
+}
+
+/* print private data for conntrack */
+static int gre_print_conntrack(struct seq_file *s,
+ const struct ip_conntrack *ct)
+{
+ return seq_printf(s, "timeout=%u, stream_timeout=%u ",
+ (ct->proto.gre.timeout / HZ),
+ (ct->proto.gre.stream_timeout / HZ));
+}
+
+/* Returns verdict for packet, and may modify conntrack */
+static int gre_packet(struct ip_conntrack *ct,
+ const struct sk_buff *skb,
+ enum ip_conntrack_info conntrackinfo)
+{
+ /* If we've seen traffic both ways, this is a GRE connection.
+ * Extend timeout. */
+ if (ct->status & IPS_SEEN_REPLY) {
+ ip_ct_refresh_acct(ct, conntrackinfo, skb,
+ ct->proto.gre.stream_timeout);
+ /* Also, more likely to be important, and not a probe. */
+ set_bit(IPS_ASSURED_BIT, &ct->status);
+ } else
+ ip_ct_refresh_acct(ct, conntrackinfo, skb,
+ ct->proto.gre.timeout);
+
+ return NF_ACCEPT;
+}
+
+/* Called when a new connection for this protocol found. */
+static int gre_new(struct ip_conntrack *ct,
+ const struct sk_buff *skb)
+{
+ DEBUGP(": ");
+ DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+
+ /* initialize to sane value. Ideally a conntrack helper
+ * (e.g. in case of pptp) is increasing them */
+ ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
+ ct->proto.gre.timeout = GRE_TIMEOUT;
+
+ return 1;
+}
+
+/* Called when a conntrack entry has already been removed from the hashes
+ * and is about to be deleted from memory */
+static void gre_destroy(struct ip_conntrack *ct)
+{
+ struct ip_conntrack *master = ct->master;
+ DEBUGP(" entering\n");
+
+ if (!master)
+ DEBUGP("no master !?!\n");
+ else
+ ip_ct_gre_keymap_destroy(master);
+}
+
+/* protocol helper struct */
+static struct ip_conntrack_protocol gre = {
+ .proto = IPPROTO_GRE,
+ .name = "gre",
+ .pkt_to_tuple = gre_pkt_to_tuple,
+ .invert_tuple = gre_invert_tuple,
+ .print_tuple = gre_print_tuple,
+ .print_conntrack = gre_print_conntrack,
+ .packet = gre_packet,
+ .new = gre_new,
+ .destroy = gre_destroy,
+ .me = THIS_MODULE,
+#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
+ defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
+ .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
+ .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
+#endif
+};
+
+/* ip_conntrack_proto_gre initialization */
+int __init ip_ct_proto_gre_init(void)
+{
+ int retcode;
+
+ if ((retcode = ip_conntrack_protocol_register(&gre))) {
+ printk(KERN_ERR "Unable to register conntrack protocol "
+ "helper for gre: %d\n", retcode);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void __exit ip_ct_proto_gre_fini(void)
+{
+ struct list_head *pos, *n;
+
+ /* delete all keymap entries */
+ write_lock_bh(&ip_ct_gre_lock);
+ list_for_each_safe(pos, n, &gre_keymap_list) {
+ DEBUGP("deleting keymap %p at module unload time\n", pos);
+ list_del(pos);
+ kfree(pos);
+ }
+ write_unlock_bh(&ip_ct_gre_lock);
+
+ ip_conntrack_protocol_unregister(&gre);
+}
+
+EXPORT_SYMBOL(ip_ct_gre_keymap_add);
+EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
diff --git a/net/ipv4/netfilter/ip_nat_helper_pptp.c b/net/ipv4/netfilter/ip_nat_helper_pptp.c
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_helper_pptp.c
@@ -0,0 +1,401 @@
+/*
+ * ip_nat_pptp.c - Version 3.0
+ *
+ * NAT support for PPTP (Point to Point Tunneling Protocol).
+ * PPTP is a a protocol for creating virtual private networks.
+ * It is a specification defined by Microsoft and some vendors
+ * working with Microsoft. PPTP is built on top of a modified
+ * version of the Internet Generic Routing Encapsulation Protocol.
+ * GRE is defined in RFC 1701 and RFC 1702. Documentation of
+ * PPTP can be found in RFC 2637
+ *
+ * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ *
+ * TODO: - NAT to a unique tuple, not to TCP source port
+ * (needs netfilter tuple reservation)
+ *
+ * Changes:
+ * 2002-02-10 - Version 1.3
+ * - Use ip_nat_mangle_tcp_packet() because of cloned skb's
+ * in local connections (Philip Craig <philipc@snapgear.com>)
+ * - add checks for magicCookie and pptp version
+ * - make argument list of pptp_{out,in}bound_packet() shorter
+ * - move to C99 style initializers
+ * - print version number at module loadtime
+ * 2003-09-22 - Version 1.5
+ * - use SNATed tcp sourceport as callid, since we get called before
+ * TCP header is mangled (Philip Craig <philipc@snapgear.com>)
+ * 2004-10-22 - Version 2.0
+ * - kernel 2.6.x version
+ * 2005-06-10 - Version 3.0
+ * - kernel >= 2.6.11 version,
+ * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/ip_nat_pptp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
+
+#define IP_NAT_PPTP_VERSION "3.0"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
+MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
+
+
+#if 0
+#include "ip_conntrack_pptp_priv.h"
+#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \
+ __FUNCTION__, ## args)
+#else
+#define DEBUGP(format, args...)
+#endif
+
+static void pptp_nat_expected(struct ip_conntrack *ct,
+ struct ip_conntrack_expect *exp)
+{
+ struct ip_conntrack *master = ct->master;
+ struct ip_conntrack_expect *other_exp;
+ struct ip_conntrack_tuple t;
+ struct ip_ct_pptp_master *ct_pptp_info;
+ struct ip_nat_pptp *nat_pptp_info;
+
+ ct_pptp_info = &master->help.ct_pptp_info;
+ nat_pptp_info = &master->nat.help.nat_pptp_info;
+
+ /* And here goes the grand finale of corrosion... */
+
+ if (exp->dir == IP_CT_DIR_ORIGINAL) {
+ DEBUGP("we are PNS->PAC\n");
+ /* therefore, build tuple for PAC->PNS */
+ t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
+ t.src.u.gre.key = htons(master->help.ct_pptp_info.pac_call_id);
+ t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ t.dst.u.gre.key = htons(master->help.ct_pptp_info.pns_call_id);
+ t.dst.protonum = IPPROTO_GRE;
+ } else {
+ DEBUGP("we are PAC->PNS\n");
+ /* build tuple for PNS->PAC */
+ t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+ t.src.u.gre.key =
+ htons(master->nat.help.nat_pptp_info.pns_call_id);
+ t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ t.dst.u.gre.key =
+ htons(master->nat.help.nat_pptp_info.pac_call_id);
+ t.dst.protonum = IPPROTO_GRE;
+ }
+
+ DEBUGP("trying to unexpect other dir: ");
+ DUMP_TUPLE(&t);
+ other_exp = __ip_conntrack_expect_find(&t);
+ if (other_exp) {
+ ip_conntrack_unexpect_related(other_exp);
+ DEBUGP("success\n");
+ } else {
+ DEBUGP("not found!\n");
+ }
+
+ ip_nat_follow_master(ct, exp);
+}
+
+/* outbound packets == from PNS to PAC */
+static int
+pptp_outbound_pkt(struct sk_buff **pskb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct PptpControlHeader *ctlh,
+ union pptp_ctrl_union *pptpReq)
+
+{
+ struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
+ struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
+
+ u_int16_t msg, *cid = NULL, new_callid;
+
+ new_callid = htons(ct_pptp_info->pns_call_id);
+
+ switch (msg = ntohs(ctlh->messageType)) {
+ case PPTP_OUT_CALL_REQUEST:
+ cid = &pptpReq->ocreq.callID;
+ /* FIXME: ideally we would want to reserve a call ID
+ * here. current netfilter NAT core is not able to do
+ * this :( For now we use TCP source port. This breaks
+ * multiple calls within one control session */
+
+ /* save original call ID in nat_info */
+ nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
+
+ /* don't use tcph->source since we are at a DSTmanip
+ * hook (e.g. PREROUTING) and pkt is not mangled yet */
+ new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
+
+ /* save new call ID in ct info */
+ ct_pptp_info->pns_call_id = ntohs(new_callid);
+ break;
+ case PPTP_IN_CALL_REPLY:
+ cid = &pptpReq->icreq.callID;
+ break;
+ case PPTP_CALL_CLEAR_REQUEST:
+ cid = &pptpReq->clrreq.callID;
+ break;
+ default:
+ DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
+ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
+ /* fall through */
+
+ case PPTP_SET_LINK_INFO:
+ /* only need to NAT in case PAC is behind NAT box */
+ case PPTP_START_SESSION_REQUEST:
+ case PPTP_START_SESSION_REPLY:
+ case PPTP_STOP_SESSION_REQUEST:
+ case PPTP_STOP_SESSION_REPLY:
+ case PPTP_ECHO_REQUEST:
+ case PPTP_ECHO_REPLY:
+ /* no need to alter packet */
+ return NF_ACCEPT;
+ }
+
+ /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass
+ * down to here */
+
+ IP_NF_ASSERT(cid);
+
+ DEBUGP("altering call id from 0x%04x to 0x%04x\n",
+ ntohs(*cid), ntohs(new_callid));
+
+ /* mangle packet */
+ if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+ (void *)cid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)),
+ sizeof(new_callid),
+ (char *)&new_callid,
+ sizeof(new_callid)) == 0)
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static int
+pptp_exp_gre(struct ip_conntrack_expect *expect_orig,
+ struct ip_conntrack_expect *expect_reply)
+{
+ struct ip_ct_pptp_master *ct_pptp_info =
+ &expect_orig->master->help.ct_pptp_info;
+ struct ip_nat_pptp *nat_pptp_info =
+ &expect_orig->master->nat.help.nat_pptp_info;
+
+ struct ip_conntrack *ct = expect_orig->master;
+
+ struct ip_conntrack_tuple inv_t;
+ struct ip_conntrack_tuple *orig_t, *reply_t;
+
+ /* save original PAC call ID in nat_info */
+ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
+
+ /* alter expectation */
+ orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+ reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+
+ /* alter expectation for PNS->PAC direction */
+ invert_tuplepr(&inv_t, &expect_orig->tuple);
+ expect_orig->saved_proto.gre.key = htons(nat_pptp_info->pac_call_id);
+ expect_orig->tuple.src.u.gre.key = htons(nat_pptp_info->pns_call_id);
+ expect_orig->tuple.dst.u.gre.key = htons(ct_pptp_info->pac_call_id);
+ inv_t.src.ip = reply_t->src.ip;
+ inv_t.dst.ip = reply_t->dst.ip;
+ inv_t.src.u.gre.key = htons(nat_pptp_info->pac_call_id);
+ inv_t.dst.u.gre.key = htons(ct_pptp_info->pns_call_id);
+
+ if (!ip_conntrack_expect_related(expect_orig)) {
+ DEBUGP("successfully registered expect\n");
+ } else {
+ DEBUGP("can't expect_related(expect_orig)\n");
+ ip_conntrack_expect_put(expect_orig);
+ return 1;
+ }
+
+ /* alter expectation for PAC->PNS direction */
+ invert_tuplepr(&inv_t, &expect_reply->tuple);
+ expect_reply->saved_proto.gre.key = htons(nat_pptp_info->pns_call_id);
+ expect_reply->tuple.src.u.gre.key = htons(nat_pptp_info->pac_call_id);
+ expect_reply->tuple.dst.u.gre.key = htons(ct_pptp_info->pns_call_id);
+ inv_t.src.ip = orig_t->src.ip;
+ inv_t.dst.ip = orig_t->dst.ip;
+ inv_t.src.u.gre.key = htons(nat_pptp_info->pns_call_id);
+ inv_t.dst.u.gre.key = htons(ct_pptp_info->pac_call_id);
+
+ if (!ip_conntrack_expect_related(expect_reply)) {
+ DEBUGP("successfully registered expect\n");
+ } else {
+ DEBUGP("can't expect_related(expect_reply)\n");
+ ip_conntrack_unexpect_related(expect_orig);
+ ip_conntrack_expect_put(expect_reply);
+ return 1;
+ }
+
+ if (ip_ct_gre_keymap_add(ct, &expect_reply->tuple, 0) < 0) {
+ DEBUGP("can't register original keymap\n");
+ ip_conntrack_unexpect_related(expect_orig);
+ ip_conntrack_unexpect_related(expect_reply);
+ return 1;
+ }
+
+ if (ip_ct_gre_keymap_add(ct, &inv_t, 1) < 0) {
+ DEBUGP("can't register reply keymap\n");
+ ip_conntrack_unexpect_related(expect_orig);
+ ip_conntrack_unexpect_related(expect_reply);
+ ip_ct_gre_keymap_destroy(ct);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* inbound packets == from PAC to PNS */
+static int
+pptp_inbound_pkt(struct sk_buff **pskb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct PptpControlHeader *ctlh,
+ union pptp_ctrl_union *pptpReq)
+{
+ struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
+ u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
+
+ int ret = NF_ACCEPT, rv;
+
+ new_pcid = htons(nat_pptp_info->pns_call_id);
+
+ switch (msg = ntohs(ctlh->messageType)) {
+ case PPTP_OUT_CALL_REPLY:
+ pcid = &pptpReq->ocack.peersCallID;
+ cid = &pptpReq->ocack.callID;
+ break;
+ case PPTP_IN_CALL_CONNECT:
+ pcid = &pptpReq->iccon.peersCallID;
+ break;
+ case PPTP_IN_CALL_REQUEST:
+ /* only need to nat in case PAC is behind NAT box */
+ break;
+ case PPTP_WAN_ERROR_NOTIFY:
+ pcid = &pptpReq->wanerr.peersCallID;
+ break;
+ case PPTP_CALL_DISCONNECT_NOTIFY:
+ pcid = &pptpReq->disc.callID;
+ break;
+ case PPTP_SET_LINK_INFO:
+ pcid = &pptpReq->setlink.peersCallID;
+ break;
+
+ default:
+ DEBUGP("unknown inbound packet %s\n",
+ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
+ /* fall through */
+
+ case PPTP_START_SESSION_REQUEST:
+ case PPTP_START_SESSION_REPLY:
+ case PPTP_STOP_SESSION_REQUEST:
+ case PPTP_STOP_SESSION_REPLY:
+ case PPTP_ECHO_REQUEST:
+ case PPTP_ECHO_REPLY:
+ /* no need to alter packet */
+ return NF_ACCEPT;
+ }
+
+ /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST,
+ * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */
+
+ /* mangle packet */
+ IP_NF_ASSERT(pcid);
+ DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
+ ntohs(*pcid), ntohs(new_pcid));
+
+ rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+ (void *)pcid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)),
+ sizeof(new_pcid), (char *)&new_pcid,
+ sizeof(new_pcid));
+ if (rv != NF_ACCEPT)
+ return rv;
+
+ if (new_cid) {
+ IP_NF_ASSERT(cid);
+ DEBUGP("altering call id from 0x%04x to 0x%04x\n",
+ ntohs(*cid), ntohs(new_cid));
+ rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+ (void *)cid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)),
+ sizeof(new_cid),
+ (char *)&new_cid,
+ sizeof(new_cid));
+ if (rv != NF_ACCEPT)
+ return rv;
+ }
+
+ /* check for earlier return value of 'switch' above */
+ if (ret != NF_ACCEPT)
+ return ret;
+
+ /* great, at least we don't need to resize packets */
+ return NF_ACCEPT;
+}
+
+
+extern int __init ip_nat_proto_gre_init(void);
+extern void __exit ip_nat_proto_gre_fini(void);
+
+static int __init init(void)
+{
+ int ret;
+
+ DEBUGP("%s: registering NAT helper\n", __FILE__);
+
+ ret = ip_nat_proto_gre_init();
+ if (ret < 0)
+ return ret;
+
+ BUG_ON(ip_nat_pptp_hook_outbound);
+ ip_nat_pptp_hook_outbound = &pptp_outbound_pkt;
+
+ BUG_ON(ip_nat_pptp_hook_inbound);
+ ip_nat_pptp_hook_inbound = &pptp_inbound_pkt;
+
+ BUG_ON(ip_nat_pptp_hook_exp_gre);
+ ip_nat_pptp_hook_exp_gre = &pptp_exp_gre;
+
+ BUG_ON(ip_nat_pptp_hook_expectfn);
+ ip_nat_pptp_hook_expectfn = &pptp_nat_expected;
+
+ printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ DEBUGP("cleanup_module\n" );
+
+ ip_nat_pptp_hook_expectfn = NULL;
+ ip_nat_pptp_hook_exp_gre = NULL;
+ ip_nat_pptp_hook_inbound = NULL;
+ ip_nat_pptp_hook_outbound = NULL;
+
+ ip_nat_proto_gre_fini();
+ /* Make sure noone calls it, meanwhile */
+ synchronize_net();
+
+ printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_proto_gre.c
@@ -0,0 +1,216 @@
+/*
+ * ip_nat_proto_gre.c - Version 2.0
+ *
+ * NAT protocol helper module for GRE.
+ *
+ * GRE is a generic encapsulation protocol, which is generally not very
+ * suited for NAT, as it has no protocol-specific part as port numbers.
+ *
+ * It has an optional key field, which may help us distinguishing two
+ * connections between the same two hosts.
+ *
+ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
+ *
+ * PPTP is built on top of a modified version of GRE, and has a mandatory
+ * field called "CallID", which serves us for the same purpose as the key
+ * field in plain GRE.
+ *
+ * Documentation about PPTP can be found in RFC 2637
+ *
+ * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
+MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
+
+#if 0
+#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \
+ __FUNCTION__, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+/* is key in given range between min and max */
+static int
+gre_in_range(const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type maniptype,
+ const union ip_conntrack_manip_proto *min,
+ const union ip_conntrack_manip_proto *max)
+{
+ u_int32_t key;
+
+ if (maniptype == IP_NAT_MANIP_SRC)
+ key = tuple->src.u.gre.key;
+ else
+ key = tuple->dst.u.gre.key;
+
+ return ntohl(key) >= ntohl(min->gre.key)
+ && ntohl(key) <= ntohl(max->gre.key);
+}
+
+/* generate unique tuple ... */
+static int
+gre_unique_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_range *range,
+ enum ip_nat_manip_type maniptype,
+ const struct ip_conntrack *conntrack)
+{
+ u_int32_t min, i, range_size;
+ u_int16_t key = 0, *keyptr;
+
+ if (maniptype == IP_NAT_MANIP_SRC)
+ keyptr = &tuple->src.u.gre.key;
+ else
+ keyptr = &tuple->dst.u.gre.key;
+
+ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
+ DEBUGP("%p: NATing GRE PPTP\n", conntrack);
+ min = 1;
+ range_size = 0xffff;
+ } else {
+ min = ntohl(range->min.gre.key);
+ range_size = ntohl(range->max.gre.key) - min + 1;
+ }
+
+ DEBUGP("min = %u, range_size = %u\n", min, range_size);
+
+ for (i = 0; i < range_size; i++, key++) {
+ *keyptr = htonl(min + key % range_size);
+ if (!ip_nat_used_tuple(tuple, conntrack))
+ return 1;
+ }
+
+ DEBUGP("%p: no NAT mapping\n", conntrack);
+
+ return 0;
+}
+
+/* manipulate a GRE packet according to maniptype */
+static int
+gre_manip_pkt(struct sk_buff **pskb,
+ unsigned int iphdroff,
+ const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type maniptype)
+{
+ struct gre_hdr *greh;
+ struct gre_hdr_pptp *pgreh;
+ struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff);
+ unsigned int hdroff = iphdroff + iph->ihl*4;
+
+ /* pgreh includes two optional 32bit fields which are not required
+ * to be there. That's where the magic '8' comes from */
+ if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh)-8))
+ return 0;
+
+ greh = (void *)(*pskb)->data + hdroff;
+ pgreh = (struct gre_hdr_pptp *) greh;
+
+ /* we only have destination manip of a packet, since 'source key'
+ * is not present in the packet itself */
+ if (maniptype == IP_NAT_MANIP_DST) {
+ /* key manipulation is always dest */
+ switch (greh->version) {
+ case 0:
+ if (!greh->key) {
+ DEBUGP("can't nat GRE w/o key\n");
+ break;
+ }
+ if (greh->csum) {
+ /* FIXME: Never tested this code... */
+ *(gre_csum(greh)) =
+ ip_nat_cheat_check(~*(gre_key(greh)),
+ tuple->dst.u.gre.key,
+ *(gre_csum(greh)));
+ }
+ *(gre_key(greh)) = tuple->dst.u.gre.key;
+ break;
+ case GRE_VERSION_PPTP:
+ DEBUGP("call_id -> 0x%04x\n",
+ ntohl(tuple->dst.u.gre.key));
+ pgreh->call_id = htons(ntohl(tuple->dst.u.gre.key));
+ break;
+ default:
+ DEBUGP("can't nat unknown GRE version\n");
+ return 0;
+ break;
+ }
+ }
+ return 1;
+}
+
+/* print out a nat tuple */
+static unsigned int
+gre_print(char *buffer,
+ const struct ip_conntrack_tuple *match,
+ const struct ip_conntrack_tuple *mask)
+{
+ unsigned int len = 0;
+
+ if (mask->src.u.gre.key)
+ len += sprintf(buffer + len, "srckey=0x%x ",
+ ntohl(match->src.u.gre.key));
+
+ if (mask->dst.u.gre.key)
+ len += sprintf(buffer + len, "dstkey=0x%x ",
+ ntohl(match->src.u.gre.key));
+
+ return len;
+}
+
+/* print a range of keys */
+static unsigned int
+gre_print_range(char *buffer, const struct ip_nat_range *range)
+{
+ if (range->min.gre.key != 0
+ || range->max.gre.key != 0xFFFF) {
+ if (range->min.gre.key == range->max.gre.key)
+ return sprintf(buffer, "key 0x%x ",
+ ntohl(range->min.gre.key));
+ else
+ return sprintf(buffer, "keys 0x%u-0x%u ",
+ ntohl(range->min.gre.key),
+ ntohl(range->max.gre.key));
+ } else
+ return 0;
+}
+
+/* nat helper struct */
+static struct ip_nat_protocol gre = {
+ .name = "GRE",
+ .protonum = IPPROTO_GRE,
+ .manip_pkt = gre_manip_pkt,
+ .in_range = gre_in_range,
+ .unique_tuple = gre_unique_tuple,
+ .print = gre_print,
+ .print_range = gre_print_range,
+#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
+ defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
+ .range_to_nfattr = ip_nat_port_range_to_nfattr,
+ .nfattr_to_range = ip_nat_port_nfattr_to_range,
+#endif
+};
+
+int __init ip_nat_proto_gre_init(void)
+{
+ if (ip_nat_protocol_register(&gre))
+ return -EIO;
+
+ return 0;
+}
+
+void __exit ip_nat_proto_gre_fini(void)
+{
+ ip_nat_protocol_unregister(&gre);
+}
diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c
--- a/net/ipv4/netfilter/ip_nat_standalone.c
+++ b/net/ipv4/netfilter/ip_nat_standalone.c
@@ -405,4 +405,6 @@ EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
EXPORT_SYMBOL(ip_nat_used_tuple);
EXPORT_SYMBOL(ip_nat_follow_master);
+EXPORT_SYMBOL_GPL(ip_nat_port_nfattr_to_range);
+EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr);
MODULE_LICENSE("GPL");
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH 2/3] NETFILTER: CLUSTERIP use bitmap for node list
From: Harald Welte @ 2005-09-15 14:52 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1: Type: text/plain, Size: 8669 bytes --]
Hi Dave, please push this optimization to mainline.
CLUSTERIP: use a bitmap to store node responsibility data
Instead of maintaining an array containing a list of nodes this instance
is responsible for let's use a simple bitmap. This provides the
following features:
* clusterip_responsible() and the add_node()/delete_node() operations
become very simple and don't need locking
* the config structure is much smaller
In spite of the completely different internal data representation the
user-space interface remains almost unchanged; the only difference is
that the proc file does not list nodes in the order they were added.
(The target info structure remains the same.)
Signed-off-by: KOVACS Krisztian <hidden@balabit.hu>
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 6a31666e4f67a3eec97274b4aa8c245d06cd4fd3
tree e3ca62b62871d4836b391261c4fc304585b24242
parent 8d5367b6545c19e551564c26adc808861624ef15
author Harald Welte <laforge@netfilter.org> Di, 13 Sep 2005 14:56:57 +0200
committer Harald Welte <laforge@netfilter.org> Di, 13 Sep 2005 14:56:57 +0200
net/ipv4/netfilter/ipt_CLUSTERIP.c | 143 +++++++++++++++---------------------
1 files changed, 61 insertions(+), 82 deletions(-)
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -13,6 +13,7 @@
#include <linux/config.h>
#include <linux/proc_fs.h>
#include <linux/jhash.h>
+#include <linux/bitops.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
@@ -30,7 +31,7 @@
#include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
-#define CLUSTERIP_VERSION "0.7"
+#define CLUSTERIP_VERSION "0.8"
#define DEBUG_CLUSTERIP
@@ -56,8 +57,7 @@ struct clusterip_config {
u_int8_t clustermac[ETH_ALEN]; /* the MAC address */
struct net_device *dev; /* device */
u_int16_t num_total_nodes; /* total number of nodes */
- u_int16_t num_local_nodes; /* number of local nodes */
- u_int16_t local_nodes[CLUSTERIP_MAX_NODES]; /* node number array */
+ unsigned long local_nodes; /* node number array */
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *pde; /* proc dir entry */
@@ -68,8 +68,7 @@ struct clusterip_config {
static LIST_HEAD(clusterip_configs);
-/* clusterip_lock protects the clusterip_configs list _AND_ the configurable
- * data within all structurses (num_local_nodes, local_nodes[]) */
+/* clusterip_lock protects the clusterip_configs list */
static DEFINE_RWLOCK(clusterip_lock);
#ifdef CONFIG_PROC_FS
@@ -156,6 +155,17 @@ clusterip_config_find_get(u_int32_t clus
return c;
}
+static void
+clusterip_config_init_nodelist(struct clusterip_config *c,
+ const struct ipt_clusterip_tgt_info *i)
+{
+ int n;
+
+ for (n = 0; n < i->num_local_nodes; n++) {
+ set_bit(i->local_nodes[n] - 1, &c->local_nodes);
+ }
+}
+
static struct clusterip_config *
clusterip_config_init(struct ipt_clusterip_tgt_info *i, u_int32_t ip,
struct net_device *dev)
@@ -172,8 +182,7 @@ clusterip_config_init(struct ipt_cluster
c->clusterip = ip;
memcpy(&c->clustermac, &i->clustermac, ETH_ALEN);
c->num_total_nodes = i->num_total_nodes;
- c->num_local_nodes = i->num_local_nodes;
- memcpy(&c->local_nodes, &i->local_nodes, sizeof(c->local_nodes));
+ clusterip_config_init_nodelist(c, i);
c->hash_mode = i->hash_mode;
c->hash_initval = i->hash_initval;
atomic_set(&c->refcount, 1);
@@ -201,53 +210,28 @@ clusterip_config_init(struct ipt_cluster
static int
clusterip_add_node(struct clusterip_config *c, u_int16_t nodenum)
{
- int i;
-
- write_lock_bh(&clusterip_lock);
- if (c->num_local_nodes >= CLUSTERIP_MAX_NODES
- || nodenum > CLUSTERIP_MAX_NODES) {
- write_unlock_bh(&clusterip_lock);
+ if (nodenum == 0 ||
+ nodenum > c->num_total_nodes)
return 1;
- }
-
- /* check if we alrady have this number in our array */
- for (i = 0; i < c->num_local_nodes; i++) {
- if (c->local_nodes[i] == nodenum) {
- write_unlock_bh(&clusterip_lock);
- return 1;
- }
- }
- c->local_nodes[c->num_local_nodes++] = nodenum;
+ /* check if we already have this number in our bitfield */
+ if (test_and_set_bit(nodenum - 1, &c->local_nodes))
+ return 1;
- write_unlock_bh(&clusterip_lock);
return 0;
}
static int
clusterip_del_node(struct clusterip_config *c, u_int16_t nodenum)
{
- int i;
-
- write_lock_bh(&clusterip_lock);
-
- if (c->num_local_nodes <= 1 || nodenum > CLUSTERIP_MAX_NODES) {
- write_unlock_bh(&clusterip_lock);
+ if (nodenum == 0 ||
+ nodenum > c->num_total_nodes)
return 1;
- }
- for (i = 0; i < c->num_local_nodes; i++) {
- if (c->local_nodes[i] == nodenum) {
- int size = sizeof(u_int16_t)*(c->num_local_nodes-(i+1));
- memmove(&c->local_nodes[i], &c->local_nodes[i+1], size);
- c->num_local_nodes--;
- write_unlock_bh(&clusterip_lock);
- return 0;
- }
- }
+ if (test_and_clear_bit(nodenum - 1, &c->local_nodes))
+ return 0;
- write_unlock_bh(&clusterip_lock);
return 1;
}
@@ -315,25 +299,7 @@ clusterip_hashfn(struct sk_buff *skb, st
static inline int
clusterip_responsible(struct clusterip_config *config, u_int32_t hash)
{
- int i;
-
- read_lock_bh(&clusterip_lock);
-
- if (config->num_local_nodes == 0) {
- read_unlock_bh(&clusterip_lock);
- return 0;
- }
-
- for (i = 0; i < config->num_local_nodes; i++) {
- if (config->local_nodes[i] == hash) {
- read_unlock_bh(&clusterip_lock);
- return 1;
- }
- }
-
- read_unlock_bh(&clusterip_lock);
-
- return 0;
+ return test_bit(hash - 1, &config->local_nodes);
}
/***********************************************************************
@@ -618,56 +584,69 @@ static struct nf_hook_ops cip_arp_ops =
#ifdef CONFIG_PROC_FS
+struct clusterip_seq_position {
+ unsigned int pos; /* position */
+ unsigned int weight; /* number of bits set == size */
+ unsigned int bit; /* current bit */
+ unsigned long val; /* current value */
+};
+
static void *clusterip_seq_start(struct seq_file *s, loff_t *pos)
{
struct proc_dir_entry *pde = s->private;
struct clusterip_config *c = pde->data;
- unsigned int *nodeidx;
-
- read_lock_bh(&clusterip_lock);
- if (*pos >= c->num_local_nodes)
+ unsigned int weight;
+ u_int32_t local_nodes;
+ struct clusterip_seq_position *idx;
+
+ /* FIXME: possible race */
+ local_nodes = c->local_nodes;
+ weight = hweight32(local_nodes);
+ if (*pos >= weight)
return NULL;
- nodeidx = kmalloc(sizeof(unsigned int), GFP_KERNEL);
- if (!nodeidx)
+ idx = kmalloc(sizeof(struct clusterip_seq_position), GFP_KERNEL);
+ if (!idx)
return ERR_PTR(-ENOMEM);
- *nodeidx = *pos;
- return nodeidx;
+ idx->pos = *pos;
+ idx->weight = weight;
+ idx->bit = ffs(local_nodes);
+ idx->val = local_nodes;
+ clear_bit(idx->bit - 1, &idx->val);
+
+ return idx;
}
static void *clusterip_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct proc_dir_entry *pde = s->private;
- struct clusterip_config *c = pde->data;
- unsigned int *nodeidx = (unsigned int *)v;
+ struct clusterip_seq_position *idx = (struct clusterip_seq_position *)v;
- *pos = ++(*nodeidx);
- if (*pos >= c->num_local_nodes) {
+ *pos = ++idx->pos;
+ if (*pos >= idx->weight) {
kfree(v);
return NULL;
}
- return nodeidx;
+ idx->bit = ffs(idx->val);
+ clear_bit(idx->bit - 1, &idx->val);
+ return idx;
}
static void clusterip_seq_stop(struct seq_file *s, void *v)
{
kfree(v);
-
- read_unlock_bh(&clusterip_lock);
}
static int clusterip_seq_show(struct seq_file *s, void *v)
{
- struct proc_dir_entry *pde = s->private;
- struct clusterip_config *c = pde->data;
- unsigned int *nodeidx = (unsigned int *)v;
+ struct clusterip_seq_position *idx = (struct clusterip_seq_position *)v;
- if (*nodeidx != 0)
+ if (idx->pos != 0)
seq_putc(s, ',');
- seq_printf(s, "%u", c->local_nodes[*nodeidx]);
- if (*nodeidx == c->num_local_nodes-1)
+ seq_printf(s, "%u", idx->bit);
+
+ if (idx->pos == idx->weight - 1)
seq_putc(s, '\n');
return 0;
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH 1/3] NETFILTER: CLUSTERIP entry refcounting
From: Harald Welte @ 2005-09-15 14:51 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1: Type: text/plain, Size: 6847 bytes --]
Hi Dave, please push this bugfix to mainline.
CLUSTERIP: introduce reference counting for entries
The CLUSTERIP target creates a procfs entry for all different cluster
IPs. Although more than one rules can refer to a single cluster IP (and
thus a single config structure), removal of the procfs entry is done
unconditionally in destroy(). In more complicated situations involving
deferred dereferencing of the config structure by procfs and creating a
new rule with the same cluster IP it's also possible that no entry will
be created for the new rule.
This patch fixes the problem by counting the number of entries
referencing a given config structure and moving the config list
manipulation and procfs entry deletion parts to the
clusterip_config_entry_put() function.
Signed-off-by: KOVACS Krisztian <hidden@balabit.hu>
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 8d5367b6545c19e551564c26adc808861624ef15
tree 33e3c99394a7f9fae6cc09e5f328144f4786c9f0
parent 8e662a0c0b3aa1257641dd32d5c92d880c110623
author Harald Welte <laforge@netfilter.org> Di, 13 Sep 2005 14:53:17 +0200
committer Harald Welte <laforge@netfilter.org> Di, 13 Sep 2005 14:53:17 +0200
net/ipv4/netfilter/ipt_CLUSTERIP.c | 80 ++++++++++++++++++++++++++++--------
1 files changed, 62 insertions(+), 18 deletions(-)
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -49,6 +49,8 @@ MODULE_DESCRIPTION("iptables target for
struct clusterip_config {
struct list_head list; /* list of all configs */
atomic_t refcount; /* reference count */
+ atomic_t entries; /* number of entries/rules
+ * referencing us */
u_int32_t clusterip; /* the IP address */
u_int8_t clustermac[ETH_ALEN]; /* the MAC address */
@@ -76,23 +78,48 @@ static struct proc_dir_entry *clusterip_
#endif
static inline void
-clusterip_config_get(struct clusterip_config *c) {
+clusterip_config_get(struct clusterip_config *c)
+{
atomic_inc(&c->refcount);
}
static inline void
-clusterip_config_put(struct clusterip_config *c) {
- if (atomic_dec_and_test(&c->refcount)) {
+clusterip_config_put(struct clusterip_config *c)
+{
+ if (atomic_dec_and_test(&c->refcount))
+ kfree(c);
+}
+
+/* increase the count of entries(rules) using/referencing this config */
+static inline void
+clusterip_config_entry_get(struct clusterip_config *c)
+{
+ atomic_inc(&c->entries);
+}
+
+/* decrease the count of entries using/referencing this config. If last
+ * entry(rule) is removed, remove the config from lists, but don't free it
+ * yet, since proc-files could still be holding references */
+static inline void
+clusterip_config_entry_put(struct clusterip_config *c)
+{
+ if (atomic_dec_and_test(&c->entries)) {
write_lock_bh(&clusterip_lock);
list_del(&c->list);
write_unlock_bh(&clusterip_lock);
+
dev_mc_delete(c->dev, c->clustermac, ETH_ALEN, 0);
dev_put(c->dev);
- kfree(c);
+
+ /* In case anyone still accesses the file, the open/close
+ * functions are also incrementing the refcount on their own,
+ * so it's safe to remove the entry even if it's in use. */
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(c->pde->name, c->pde->parent);
+#endif
}
}
-
static struct clusterip_config *
__clusterip_config_find(u_int32_t clusterip)
{
@@ -111,7 +138,7 @@ __clusterip_config_find(u_int32_t cluste
}
static inline struct clusterip_config *
-clusterip_config_find_get(u_int32_t clusterip)
+clusterip_config_find_get(u_int32_t clusterip, int entry)
{
struct clusterip_config *c;
@@ -122,6 +149,8 @@ clusterip_config_find_get(u_int32_t clus
return NULL;
}
atomic_inc(&c->refcount);
+ if (entry)
+ atomic_inc(&c->entries);
read_unlock_bh(&clusterip_lock);
return c;
@@ -148,6 +177,7 @@ clusterip_config_init(struct ipt_cluster
c->hash_mode = i->hash_mode;
c->hash_initval = i->hash_initval;
atomic_set(&c->refcount, 1);
+ atomic_set(&c->entries, 1);
#ifdef CONFIG_PROC_FS
/* create proc dir entry */
@@ -415,8 +445,26 @@ checkentry(const char *tablename,
/* FIXME: further sanity checks */
- config = clusterip_config_find_get(e->ip.dst.s_addr);
- if (!config) {
+ config = clusterip_config_find_get(e->ip.dst.s_addr, 1);
+ if (config) {
+ if (cipinfo->config != NULL) {
+ /* Case A: This is an entry that gets reloaded, since
+ * it still has a cipinfo->config pointer. Simply
+ * increase the entry refcount and return */
+ if (cipinfo->config != config) {
+ printk(KERN_ERR "CLUSTERIP: Reloaded entry "
+ "has invalid config pointer!\n");
+ return 0;
+ }
+ clusterip_config_entry_get(cipinfo->config);
+ } else {
+ /* Case B: This is a new rule referring to an existing
+ * clusterip config. */
+ cipinfo->config = config;
+ clusterip_config_entry_get(cipinfo->config);
+ }
+ } else {
+ /* Case C: This is a completely new clusterip config */
if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
printk(KERN_WARNING "CLUSTERIP: no config found for %u.%u.%u.%u, need 'new'\n", NIPQUAD(e->ip.dst.s_addr));
return 0;
@@ -443,10 +491,9 @@ checkentry(const char *tablename,
}
dev_mc_add(config->dev,config->clustermac, ETH_ALEN, 0);
}
+ cipinfo->config = config;
}
- cipinfo->config = config;
-
return 1;
}
@@ -455,13 +502,10 @@ static void destroy(void *matchinfo, uns
{
struct ipt_clusterip_tgt_info *cipinfo = matchinfo;
- /* we first remove the proc entry and then drop the reference
- * count. In case anyone still accesses the file, the open/close
- * functions are also incrementing the refcount on their own */
-#ifdef CONFIG_PROC_FS
- remove_proc_entry(cipinfo->config->pde->name,
- cipinfo->config->pde->parent);
-#endif
+ /* if no more entries are referencing the config, remove it
+ * from the list and destroy the proc entry */
+ clusterip_config_entry_put(cipinfo->config);
+
clusterip_config_put(cipinfo->config);
}
@@ -533,7 +577,7 @@ arp_mangle(unsigned int hook,
/* if there is no clusterip configuration for the arp reply's
* source ip, we don't want to mangle it */
- c = clusterip_config_find_get(payload->src_ip);
+ c = clusterip_config_find_get(payload->src_ip, 0);
if (!c)
return NF_ACCEPT;
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [RFC] hippi: change to not use skb private
From: David S. Miller @ 2005-09-15 4:03 UTC (permalink / raw)
To: shemminger; +Cc: jes, netdev, linux-kernel
In-Reply-To: <20050913113858.440d3a0f@localhost.localdomain>
From: Stephen Hemminger <shemminger@osdl.org>
Date: Tue, 13 Sep 2005 11:38:58 -0700
> It looks like the following would fix hippi to not have to put
> fields in sk_buff. The ifield looks appears to to be additional
> header information that is being passed in the skb but could just
> be put in the header.
Stephen this patch is against 2.6.13 or something. We already
put this thing into a SKB control block for 2.6.14-rc1. Do you
want to keep things that way or update your patch for 2.6.14-rc1?
^ permalink raw reply
* Re: VPN server over windows XP
From: Redeeman @ 2005-09-14 20:29 UTC (permalink / raw)
To: Alaa Dalghan
Cc: linux-crypto, linux-kernel, linux-net, linux-security-module,
netdev
In-Reply-To: <BAY106-F29965CB20274069D3A5BA5AB9F0@phx.gbl>
On Wed, 2005-09-14 at 20:18 +0000, Alaa Dalghan wrote:
> hello everyone,
<snip>
this really isnt the place for this discussion
>
> Alaadin
>
> _________________________________________________________________
> Express yourself instantly with MSN Messenger! Download today - it's FREE!
> http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
^ permalink raw reply
* VPN server over windows XP
From: Alaa Dalghan @ 2005-09-14 20:18 UTC (permalink / raw)
To: linux-crypto, linux-kernel, linux-net, linux-security-module,
netdev
hello everyone,
I am trying to setup a windows xp machine as a vpn server that accepts
multiple ipsec tunnels from other windows xp machines.
My restrictions are the following:
1- I need to set the vpn server on windows XP (not windows 2000 server, nor
2003, nor ISA server, etc.)
2- I need to use tunnel mode ipsec
3- The vpn server should accept MULTIPLE vpn tunnels.
The first problem I faced is that windows xp does not support ipsec tunnel
mode between 2 xp machines. It only supports transport mode which is not
what I want.
To overcome this lack of IP tunneling I tried to use the built-in tunneling
capabilities such as PPTP and L2TP/ipsec, and it worked. But the problem
here is that a windows xp can not accept more than ONE SINGLE incoming
connection at a time, and I need multiple connections.
I think the solution could be one of the following:
1-Installing a third party FREE vpn server (or L2TP server) on windows XP.
If you know one please tell me.
2-Importing some features from windows 2000 server or 2003 server (some
executables or services or plugins that enable xp to run as a vpn server and
accept multiple connections). If you know what to import please tell me.
3- Installing a pure IP tunneling solution on windows xp so that it can be
combined with ipsec encryption to yield tunnel mode encryption.
I appreciate any help,
Alaadin
_________________________________________________________________
Express yourself instantly with MSN Messenger! Download today - it's FREE!
http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
-
Linux-crypto: cryptography in and on the Linux system
Archive: http://mail.nl.linux.org/linux-crypto/
^ permalink raw reply
* Re: [PATCH] gt96100: stop using pci_find_device()
From: Ralf Baechle @ 2005-09-14 19:40 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: Scott Feldman, netdev, linux-mips
In-Reply-To: <20050914185136.GE19491@mipter.zuzino.mipt.ru>
On Wed, Sep 14, 2005 at 10:51:37PM +0400, Alexey Dobriyan wrote:
> Replace pci_find_device with pci_get_device/pci_dev_put to plug race
> with pci_find_device.
The system is a little odd; the race condition probably doens't exist on
this particular chipset.
Ralf
^ permalink raw reply
* Re: Fw: masquerading failure for at least icmp and tcp+sack on amd64
From: Marc Lehmann @ 2005-09-14 19:09 UTC (permalink / raw)
To: Patrick McHardy
Cc: Andrew Morton, netdev, Netfilter Development Mailinglist,
Stephen Hemminger
In-Reply-To: <43243AB9.9000705@trash.net>
On Sun, Sep 11, 2005 at 04:10:01PM +0200, Patrick McHardy <kaber@trash.net> wrote:
> >>What network driver are you using?
> >
> >Happens with both skge and sk98.
>
> Are you sure the same checksum error happens?
No, of course not.
I have two similar network cards in that machine (a normal sk98 pci card
and an onboard one that skge calls Yukon lite and the sk98lin driver calls
9521. The onboard one does not, though, work with sk98lin).
What I did test was with skge and the onboard card, and sk98_lin with
the pci card, and in both cases I couldn't get masquerading to work, but
everything else worked fine.
> ip_summed to CHECKSUM_HW, it uses either CHECKSUM_UNNECESSARY for
> HW checksummed packets, in which case the check is skipped in
> ip_conntrack, or it uses CHECKSUM_NONE, in which case the checksum
> in the packet must be invalid if the check fails and the packet
> wouldn't be accepted by the final receipient anyway. skge uses
> CHECKSUM_HW for every packet, so they have nothing in common wrt.
> HW checksumming. This would normally mean we can rule out HW
> checksumming, but for some reason turning it off on skge seems
> to help in your case.
... and I have not tried turning it off with sk98lin (and would like to
avoid it, as it requires reconfiguration to test). If any additional tests
arre required, I will do them, though.
> > nfs_acl 3136 1 nfsd
> > sunrpc 142632 13 nfsd,lockd,nfs_acl
> >
> >However, it happens with init=/bin/bash and loading the single iptables
> >rule I sent in the original report, for either eth0/1 or the ppp
> >interface. I can send you my 278 other rules if you want, but they have
> >had no effect on the problem here.
>
> No, thanks. But your entire .config might help ..
Sorry for the delay :( It's here:
http://data.plan9.de/config.doom
This is the currently-running 2.6.13 config, when testing 2.6.11, i just
did "make oldconfig" on this one.
--
The choice of a
-----==- _GNU_
----==-- _ generation Marc Lehmann
---==---(_)__ __ ____ __ pcg@goof.com
--==---/ / _ \/ // /\ \/ / http://schmorp.de/
-=====/_/_//_/\_,_/ /_/\_\ XX11-RIPE
^ permalink raw reply
* Re: [PATCH] gt96100: stop using pci_find_device()
From: Jeff Garzik @ 2005-09-14 19:00 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: Ralf Baechle, Scott Feldman, netdev, linux-mips
In-Reply-To: <20050914185136.GE19491@mipter.zuzino.mipt.ru>
Alexey Dobriyan wrote:
> From: Scott Feldman <sfeldma@pobox.com>
>
> Replace pci_find_device with pci_get_device/pci_dev_put to plug race
> with pci_find_device.
>
> Signed-off-by: Scott Feldman <sfeldma@pobox.com>
> Signed-off-by: Maximilian Attems <janitor@sternwelten.at>
> Signed-off-by: Domen Puncer <domen@coderock.org>
> Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
> ---
>
> drivers/net/gt96100eth.c | 6 ++++--
> 1 files changed, 4 insertions(+), 2 deletions(-)
>
> --- a/drivers/net/gt96100eth.c
> +++ b/drivers/net/gt96100eth.c
> @@ -615,9 +615,9 @@ static int gt96100_init_module(void)
> /*
> * Stupid probe because this really isn't a PCI device
> */
> - if (!(pci = pci_find_device(PCI_VENDOR_ID_MARVELL,
> + if (!(pci = pci_get_device(PCI_VENDOR_ID_MARVELL,
> PCI_DEVICE_ID_MARVELL_GT96100, NULL)) &&
> - !(pci = pci_find_device(PCI_VENDOR_ID_MARVELL,
> + !(pci = pci_get_device(PCI_VENDOR_ID_MARVELL,
> PCI_DEVICE_ID_MARVELL_GT96100A, NULL))) {
> printk(KERN_ERR __FILE__ ": GT96100 not found!\n");
> return -ENODEV;
> @@ -627,12 +627,14 @@ static int gt96100_init_module(void)
> if (cpuConfig & (1<<12)) {
> printk(KERN_ERR __FILE__
> ": must be in Big Endian mode!\n");
> + pci_dev_put(pci);
> return -ENODEV;
NAK -- convert it to use standard PCI driver API.
Jeff
^ permalink raw reply
* [PATCH] gt96100: stop using pci_find_device()
From: Alexey Dobriyan @ 2005-09-14 18:51 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Scott Feldman, netdev, linux-mips
From: Scott Feldman <sfeldma@pobox.com>
Replace pci_find_device with pci_get_device/pci_dev_put to plug race
with pci_find_device.
Signed-off-by: Scott Feldman <sfeldma@pobox.com>
Signed-off-by: Maximilian Attems <janitor@sternwelten.at>
Signed-off-by: Domen Puncer <domen@coderock.org>
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---
drivers/net/gt96100eth.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
--- a/drivers/net/gt96100eth.c
+++ b/drivers/net/gt96100eth.c
@@ -615,9 +615,9 @@ static int gt96100_init_module(void)
/*
* Stupid probe because this really isn't a PCI device
*/
- if (!(pci = pci_find_device(PCI_VENDOR_ID_MARVELL,
+ if (!(pci = pci_get_device(PCI_VENDOR_ID_MARVELL,
PCI_DEVICE_ID_MARVELL_GT96100, NULL)) &&
- !(pci = pci_find_device(PCI_VENDOR_ID_MARVELL,
+ !(pci = pci_get_device(PCI_VENDOR_ID_MARVELL,
PCI_DEVICE_ID_MARVELL_GT96100A, NULL))) {
printk(KERN_ERR __FILE__ ": GT96100 not found!\n");
return -ENODEV;
@@ -627,12 +627,14 @@ static int gt96100_init_module(void)
if (cpuConfig & (1<<12)) {
printk(KERN_ERR __FILE__
": must be in Big Endian mode!\n");
+ pci_dev_put(pci);
return -ENODEV;
}
for (i=0; i < NUM_INTERFACES; i++)
retval |= gt96100_probe1(pci, i);
+ pci_dev_put(pci);
return retval;
}
^ permalink raw reply
* [PATCH] New PowerPC 4xx on-chip ethernet controller driver
From: Eugene Surovegin @ 2005-09-14 17:08 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linuxppc-embedded
In-Reply-To: <43281B7D.5000106@pobox.com>
On Wed, Sep 14, 2005 at 08:45:49AM -0400, Jeff Garzik wrote:
> Eugene Surovegin wrote:
> >Remove old PPC4xx EMAC driver
> >
> >Signed-off-by: Eugene Surovegin <ebs@ebshome.net>
>
> Please post a diff, along with a description of the changes.
>
> One huge patch to "remove emac driver" and another huge patch to "add
> emac driver" is quite silly.
This patch replaces current PowerPC 4xx EMAC driver with
new, re-written version.
New driver uses NAPI, it solves stability problems under heavy packet
load and low memory, corrects chip register access and fixes numerous
small bugs I don't even remember now :).
This patch has been tested on all supported in 2.6 PPC 4xx boards.
It's been used in production for almost a year now on custom
4xx hardware. PPC32 specific parts are already upstream.
Patch was acked by the current EMAC driver maintainer (Matt Porter). I
will be maintaining this new version.
Signed-off-by: Eugene Surovegin <ebs@ebshome.net>
--
Kconfig | 72
ibm_emac/Makefile | 13
ibm_emac/ibm_emac.h | 418 +++--
ibm_emac/ibm_emac_core.c | 3391 ++++++++++++++++++++++++----------------------
ibm_emac/ibm_emac_core.h | 313 ++--
ibm_emac/ibm_emac_debug.c | 377 ++---
ibm_emac/ibm_emac_debug.h | 63
ibm_emac/ibm_emac_mal.c | 671 +++++----
ibm_emac/ibm_emac_mal.h | 336 +++-
ibm_emac/ibm_emac_phy.c | 335 ++--
ibm_emac/ibm_emac_phy.h | 105 -
ibm_emac/ibm_emac_rgmii.c | 202 ++
ibm_emac/ibm_emac_rgmii.h | 68
ibm_emac/ibm_emac_tah.c | 111 +
ibm_emac/ibm_emac_tah.h | 96 -
ibm_emac/ibm_emac_zmii.c | 256 +++
ibm_emac/ibm_emac_zmii.h | 114 -
17 files changed, 4119 insertions(+), 2822 deletions(-)
Patch is quite big (~234K) because there is virtualy 0% of common code
between old and new version.
It can be found at http://kernel.ebshome.net/emac/4xx_napi_emac.diff
^ permalink raw reply
* Re: [PATCH 1/2] New PowerPC 4xx on-chip ethernet controller driver
From: Eugene Surovegin @ 2005-09-14 16:18 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linuxppc-embedded
In-Reply-To: <43281B7D.5000106@pobox.com>
On Wed, Sep 14, 2005 at 08:45:49AM -0400, Jeff Garzik wrote:
> Eugene Surovegin wrote:
> >Remove old PPC4xx EMAC driver
> >
> >Signed-off-by: Eugene Surovegin <ebs@ebshome.net>
>
> Please post a diff, along with a description of the changes.
>
> One huge patch to "remove emac driver" and another huge patch to "add
> emac driver" is quite silly.
Jeff, it's not silly. I can post combined patch, but you won't be
able to read it (and combined patch is still big).
As I said, it's a _complete_ re-write. It's a _new_ driver, not just
some "changes". Description of what was done was in the first e-mail.
I've split it into two parts specifically, so _you_ will be able to
read new patch and comment on it.
--
Eugene
^ permalink raw reply
* Re: [patch 3/4] s390: TSO related fixes in qeth driver
From: Jeff Garzik @ 2005-09-14 16:15 UTC (permalink / raw)
To: Frank Pavlic; +Cc: netdev, linux-kernel
In-Reply-To: <20050914160326.GA3458@pavlic>
applied patch #3 and #4, thanks for resending.
Jeff
^ permalink raw reply
* [patch 4/4] s390: qeth driver fixes
From: Frank Pavlic @ 2005-09-14 16:05 UTC (permalink / raw)
To: jgarzik; +Cc: linux-kernel, netdev
[patch 4/4] s390: qeth driver fixes .
From: Frank Pavlic <pavlic@de.ibm.com>
- Clear read channel first prior to using ccw_device_set_offline.
- use QETH_DBF_TEXT instead of QETH_DBF_SPRINTF
- invoke qeth_halt_channel and qeth_clear_channel for all channels,
even if halt/clear for one of the channel fails.
- enable qeth_arp_query function for GuestLAN devices
Signed-off-by: Frank Pavlic <pavlic@de.ibm.com>
diffstat:
qeth.h | 2 -
qeth_main.c | 106 +++++++++++++++++++++++++-----------------------------------
qeth_sys.c | 11 +++---
3 files changed, 53 insertions(+), 66 deletions(-)
diff -Naupr linux-2.6-orig/drivers/s390/net/qeth.h linux-2.6-patched/drivers/s390/net/qeth.h
--- linux-2.6-orig/drivers/s390/net/qeth.h 2005-09-05 13:53:40.000000000 +0200
+++ linux-2.6-patched/drivers/s390/net/qeth.h 2005-09-05 14:00:46.000000000 +0200
@@ -24,7 +24,7 @@
#include "qeth_mpc.h"
-#define VERSION_QETH_H "$Revision: 1.141 $"
+#define VERSION_QETH_H "$Revision: 1.142 $"
#ifdef CONFIG_QETH_IPV6
#define QETH_VERSION_IPV6 ":IPv6"
diff -Naupr linux-2.6-orig/drivers/s390/net/qeth_main.c linux-2.6-patched/drivers/s390/net/qeth_main.c
--- linux-2.6-orig/drivers/s390/net/qeth_main.c 2005-09-05 13:53:40.000000000 +0200
+++ linux-2.6-patched/drivers/s390/net/qeth_main.c 2005-09-05 14:05:43.000000000 +0200
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.219 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.224 $)
*
* Linux on zSeries OSA Express and HiperSockets support
*
@@ -12,7 +12,7 @@
* Frank Pavlic (pavlic@de.ibm.com) and
* Thomas Spatzier <tspat@de.ibm.com>
*
- * $Revision: 1.219 $ $Date: 2005/05/04 20:19:18 $
+ * $Revision: 1.224 $ $Date: 2005/05/04 20:19:18 $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,14 +29,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/***
- * eye catcher; just for debugging purposes
- */
-void volatile
-qeth_eyecatcher(void)
-{
- return;
-}
#include <linux/config.h>
#include <linux/module.h>
@@ -80,7 +72,7 @@ qeth_eyecatcher(void)
#include "qeth_eddp.h"
#include "qeth_tso.h"
-#define VERSION_QETH_C "$Revision: 1.219 $"
+#define VERSION_QETH_C "$Revision: 1.224 $"
static const char *version = "qeth S/390 OSA-Express driver";
/**
@@ -2759,11 +2751,9 @@ qeth_flush_buffers(struct qeth_qdio_out_
queue->card->perf_stats.outbound_do_qdio_start_time;
#endif
if (rc){
- QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO "
- "returned error (%i) on device %s.",
- rc, CARD_DDEV_ID(queue->card));
QETH_DBF_TEXT(trace, 2, "flushbuf");
QETH_DBF_TEXT_(trace, 2, " err%d", rc);
+ QETH_DBF_TEXT_(trace, 2, "%s", CARD_DDEV_ID(queue->card));
queue->card->stats.tx_errors += count;
/* this must not happen under normal circumstances. if it
* happens something is really wrong -> recover */
@@ -2909,11 +2899,8 @@ qeth_qdio_output_handler(struct ccw_devi
QETH_DBF_TEXT(trace, 6, "qdouhdl");
if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){
- QETH_DBF_SPRINTF(trace, 2, "On device %s: "
- "received active check "
- "condition (0x%08x).",
- CARD_BUS_ID(card), status);
- QETH_DBF_TEXT(trace, 2, "chkcond");
+ QETH_DBF_TEXT(trace, 2, "achkcond");
+ QETH_DBF_TEXT_(trace, 2, "%s", CARD_BUS_ID(card));
QETH_DBF_TEXT_(trace, 2, "%08x", status);
netif_stop_queue(card->dev);
qeth_schedule_recovery(card);
@@ -3356,26 +3343,32 @@ qeth_halt_channel(struct qeth_channel *c
static int
qeth_halt_channels(struct qeth_card *card)
{
- int rc = 0;
+ int rc1 = 0, rc2=0, rc3 = 0;
QETH_DBF_TEXT(trace,3,"haltchs");
- if ((rc = qeth_halt_channel(&card->read)))
- return rc;
- if ((rc = qeth_halt_channel(&card->write)))
- return rc;
- return qeth_halt_channel(&card->data);
+ rc1 = qeth_halt_channel(&card->read);
+ rc2 = qeth_halt_channel(&card->write);
+ rc3 = qeth_halt_channel(&card->data);
+ if (rc1)
+ return rc1;
+ if (rc2)
+ return rc2;
+ return rc3;
}
static int
qeth_clear_channels(struct qeth_card *card)
{
- int rc = 0;
+ int rc1 = 0, rc2=0, rc3 = 0;
QETH_DBF_TEXT(trace,3,"clearchs");
- if ((rc = qeth_clear_channel(&card->read)))
- return rc;
- if ((rc = qeth_clear_channel(&card->write)))
- return rc;
- return qeth_clear_channel(&card->data);
+ rc1 = qeth_clear_channel(&card->read);
+ rc2 = qeth_clear_channel(&card->write);
+ rc3 = qeth_clear_channel(&card->data);
+ if (rc1)
+ return rc1;
+ if (rc2)
+ return rc2;
+ return rc3;
}
static int
@@ -3445,23 +3438,23 @@ qeth_mpc_initialize(struct qeth_card *ca
}
if ((rc = qeth_cm_enable(card))){
QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
- return rc;
+ goto out_qdio;
}
if ((rc = qeth_cm_setup(card))){
QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
- return rc;
+ goto out_qdio;
}
if ((rc = qeth_ulp_enable(card))){
QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
- return rc;
+ goto out_qdio;
}
if ((rc = qeth_ulp_setup(card))){
QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
- return rc;
+ goto out_qdio;
}
if ((rc = qeth_alloc_qdio_buffers(card))){
QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
- return rc;
+ goto out_qdio;
}
if ((rc = qeth_qdio_establish(card))){
QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
@@ -4281,7 +4274,7 @@ qeth_send_packet(struct qeth_card *card,
int ipv = 0;
int cast_type;
struct qeth_qdio_out_q *queue;
- struct qeth_hdr *hdr;
+ struct qeth_hdr *hdr = NULL;
int elements_needed = 0;
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
@@ -4512,7 +4505,11 @@ qeth_arp_set_no_entries(struct qeth_card
QETH_DBF_TEXT(trace,3,"arpstnoe");
- /* TODO: really not supported by GuestLAN? */
+ /*
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES;
+ * thus we say EOPNOTSUPP for this ARP function
+ */
if (card->info.guestlan)
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
@@ -4689,14 +4686,6 @@ qeth_arp_query(struct qeth_card *card, c
QETH_DBF_TEXT(trace,3,"arpquery");
- /*
- * currently GuestLAN does only deliver all zeros on query arp,
- * even though arp processing is supported (according to IPA supp.
- * funcs flags); since all zeros is no valueable information,
- * we say EOPNOTSUPP for all ARP functions
- */
- /*if (card->info.guestlan)
- return -EOPNOTSUPP; */
if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
@@ -4902,10 +4891,9 @@ qeth_arp_add_entry(struct qeth_card *car
QETH_DBF_TEXT(trace,3,"arpadent");
/*
- * currently GuestLAN does only deliver all zeros on query arp,
- * even though arp processing is supported (according to IPA supp.
- * funcs flags); since all zeros is no valueable information,
- * we say EOPNOTSUPP for all ARP functions
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY;
+ * thus we say EOPNOTSUPP for this ARP function
*/
if (card->info.guestlan)
return -EOPNOTSUPP;
@@ -4945,10 +4933,9 @@ qeth_arp_remove_entry(struct qeth_card *
QETH_DBF_TEXT(trace,3,"arprment");
/*
- * currently GuestLAN does only deliver all zeros on query arp,
- * even though arp processing is supported (according to IPA supp.
- * funcs flags); since all zeros is no valueable information,
- * we say EOPNOTSUPP for all ARP functions
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_REMOVE_ENTRY;
+ * thus we say EOPNOTSUPP for this ARP function
*/
if (card->info.guestlan)
return -EOPNOTSUPP;
@@ -4986,11 +4973,10 @@ qeth_arp_flush_cache(struct qeth_card *c
QETH_DBF_TEXT(trace,3,"arpflush");
/*
- * currently GuestLAN does only deliver all zeros on query arp,
- * even though arp processing is supported (according to IPA supp.
- * funcs flags); since all zeros is no valueable information,
- * we say EOPNOTSUPP for all ARP functions
- */
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE;
+ * thus we say EOPNOTSUPP for this ARP function
+ */
if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
@@ -8266,7 +8252,6 @@ qeth_init(void)
{
int rc=0;
- qeth_eyecatcher();
PRINT_INFO("loading %s (%s/%s/%s/%s/%s/%s/%s %s %s)\n",
version, VERSION_QETH_C, VERSION_QETH_H,
VERSION_QETH_MPC_H, VERSION_QETH_MPC_C,
@@ -8347,7 +8332,6 @@ again:
printk("qeth: removed\n");
}
-EXPORT_SYMBOL(qeth_eyecatcher);
module_init(qeth_init);
module_exit(qeth_exit);
MODULE_AUTHOR("Frank Pavlic <pavlic@de.ibm.com>");
diff -Naupr linux-2.6-orig/drivers/s390/net/qeth_sys.c linux-2.6-patched/drivers/s390/net/qeth_sys.c
--- linux-2.6-orig/drivers/s390/net/qeth_sys.c 2005-09-05 13:53:40.000000000 +0200
+++ linux-2.6-patched/drivers/s390/net/qeth_sys.c 2005-09-05 14:00:46.000000000 +0200
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.53 $)
+ * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.54 $)
*
* Linux on zSeries OSA Express and HiperSockets support
* This file contains code related to sysfs.
@@ -20,7 +20,7 @@
#include "qeth_mpc.h"
#include "qeth_fs.h"
-const char *VERSION_QETH_SYS_C = "$Revision: 1.53 $";
+const char *VERSION_QETH_SYS_C = "$Revision: 1.54 $";
/*****************************************************************************/
/* */
@@ -722,10 +722,13 @@ qeth_dev_layer2_store(struct device *dev
if (!card)
return -EINVAL;
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ PRINT_WARN("Layer2 on Hipersockets is not supported! \n");
+ return -EPERM;
+ }
if (((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER)) ||
- (card->info.type != QETH_CARD_TYPE_OSAE))
+ (card->state != CARD_STATE_RECOVER)))
return -EPERM;
i = simple_strtoul(buf, &tmp, 16);
^ permalink raw reply
* [patch 3/4] s390: TSO related fixes in qeth driver
From: Frank Pavlic @ 2005-09-14 16:03 UTC (permalink / raw)
To: jgarzik; +Cc: netdev, linux-kernel
Jeff,
I'm sorry seems that they have not been sent out either ...
ok here they come ...
[patch 3/4] s390: TSO related fixes in qeth driver
From: Frank Pavlic <pavlic@de.ibm.com>
TSO related fixes :
- changing value of large_send attribute while network traffic
is running caused program check and thus device recovery.
- Due to hardware restriction discard packet when it exceeds 60K
otherwise qeth will cause program checks and thus traffic stall
when trying to send such huge packets.
Signed-off-by: Frank Pavlic <pavlic@de.ibm.com>
diffstat:
qeth.h | 4 ++--
qeth_main.c | 33 +++++++++++++++++++++------------
qeth_sys.c | 10 +++-------
3 files changed, 26 insertions(+), 21 deletions(-)
diff -Naupr linux-2.6-orig/drivers/s390/net/qeth.h linux-2.6-patched/drivers/s390/net/qeth.h
--- linux-2.6-orig/drivers/s390/net/qeth.h 2005-09-05 11:46:56.000000000 +0200
+++ linux-2.6-patched/drivers/s390/net/qeth.h 2005-09-05 12:16:08.000000000 +0200
@@ -24,7 +24,7 @@
#include "qeth_mpc.h"
-#define VERSION_QETH_H "$Revision: 1.139 $"
+#define VERSION_QETH_H "$Revision: 1.141 $"
#ifdef CONFIG_QETH_IPV6
#define QETH_VERSION_IPV6 ":IPv6"
@@ -1172,7 +1172,7 @@ extern int
qeth_realloc_buffer_pool(struct qeth_card *, int);
extern int
-qeth_set_large_send(struct qeth_card *);
+qeth_set_large_send(struct qeth_card *, enum qeth_large_send_types);
extern void
qeth_fill_header(struct qeth_card *, struct qeth_hdr *,
diff -Naupr linux-2.6-orig/drivers/s390/net/qeth_main.c linux-2.6-patched/drivers/s390/net/qeth_main.c
--- linux-2.6-orig/drivers/s390/net/qeth_main.c 2005-09-05 11:46:56.000000000 +0200
+++ linux-2.6-patched/drivers/s390/net/qeth_main.c 2005-09-05 12:17:38.000000000 +0200
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.219 $)
*
* Linux on zSeries OSA Express and HiperSockets support
*
@@ -12,7 +12,7 @@
* Frank Pavlic (pavlic@de.ibm.com) and
* Thomas Spatzier <tspat@de.ibm.com>
*
- * $Revision: 1.214 $ $Date: 2005/05/04 20:19:18 $
+ * $Revision: 1.219 $ $Date: 2005/05/04 20:19:18 $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -80,7 +80,7 @@ qeth_eyecatcher(void)
#include "qeth_eddp.h"
#include "qeth_tso.h"
-#define VERSION_QETH_C "$Revision: 1.214 $"
+#define VERSION_QETH_C "$Revision: 1.219 $"
static const char *version = "qeth S/390 OSA-Express driver";
/**
@@ -3795,12 +3795,16 @@ static inline int
qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
struct qeth_hdr **hdr, int ipv)
{
+ int rc;
#ifdef CONFIG_QETH_VLAN
u16 *tag;
#endif
QETH_DBF_TEXT(trace, 6, "prepskb");
+ rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr));
+ if (rc)
+ return rc;
#ifdef CONFIG_QETH_VLAN
if (card->vlangrp && vlan_tx_tag_present(*skb) &&
((ipv == 6) || card->options.layer2) ) {
@@ -4251,7 +4255,8 @@ out:
}
static inline int
-qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb)
+qeth_get_elements_no(struct qeth_card *card, void *hdr,
+ struct sk_buff *skb, int elems)
{
int elements_needed = 0;
@@ -4261,9 +4266,10 @@ qeth_get_elements_no(struct qeth_card *c
if (elements_needed == 0 )
elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
+ skb->len) >> PAGE_SHIFT);
- if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
+ if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){
PRINT_ERR("qeth_do_send_packet: invalid size of "
- "IP packet. Discarded.");
+ "IP packet (Number=%d / Length=%d). Discarded.\n",
+ (elements_needed+elems), skb->len);
return 0;
}
return elements_needed;
@@ -4337,9 +4343,11 @@ qeth_send_packet(struct qeth_card *card,
return -EINVAL;
}
} else {
- elements_needed += qeth_get_elements_no(card,(void*) hdr, skb);
- if (!elements_needed)
+ int elems = qeth_get_elements_no(card,(void*) hdr, skb,
+ elements_needed);
+ if (!elems)
return -EINVAL;
+ elements_needed += elems;
}
if (card->info.type != QETH_CARD_TYPE_IQD)
@@ -7038,14 +7046,16 @@ qeth_setrouting_v6(struct qeth_card *car
}
int
-qeth_set_large_send(struct qeth_card *card)
+qeth_set_large_send(struct qeth_card *card, enum qeth_large_send_types type)
{
int rc = 0;
- if (card->dev == NULL)
+ if (card->dev == NULL) {
+ card->options.large_send = type;
return 0;
-
+ }
netif_stop_queue(card->dev);
+ card->options.large_send = type;
switch (card->options.large_send) {
case QETH_LARGE_SEND_EDDP:
card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
@@ -7066,7 +7076,6 @@ qeth_set_large_send(struct qeth_card *ca
card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
break;
}
-
netif_wake_queue(card->dev);
return rc;
}
diff -Naupr linux-2.6-orig/drivers/s390/net/qeth_sys.c linux-2.6-patched/drivers/s390/net/qeth_sys.c
--- linux-2.6-orig/drivers/s390/net/qeth_sys.c 2005-09-05 11:46:56.000000000 +0200
+++ linux-2.6-patched/drivers/s390/net/qeth_sys.c 2005-09-05 12:14:30.000000000 +0200
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.51 $)
+ * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.53 $)
*
* Linux on zSeries OSA Express and HiperSockets support
* This file contains code related to sysfs.
@@ -20,7 +20,7 @@
#include "qeth_mpc.h"
#include "qeth_fs.h"
-const char *VERSION_QETH_SYS_C = "$Revision: 1.51 $";
+const char *VERSION_QETH_SYS_C = "$Revision: 1.53 $";
/*****************************************************************************/
/* */
@@ -771,9 +771,7 @@ qeth_dev_large_send_store(struct device
if (!card)
return -EINVAL;
-
tmp = strsep((char **) &buf, "\n");
-
if (!strcmp(tmp, "no")){
type = QETH_LARGE_SEND_NO;
} else if (!strcmp(tmp, "EDDP")) {
@@ -786,10 +784,8 @@ qeth_dev_large_send_store(struct device
}
if (card->options.large_send == type)
return count;
- card->options.large_send = type;
- if ((rc = qeth_set_large_send(card)))
+ if ((rc = qeth_set_large_send(card, type)))
return rc;
-
return count;
}
^ permalink raw reply
* Re: [git patches] net driver fixes
From: Jeff Garzik @ 2005-09-14 15:40 UTC (permalink / raw)
To: Frank Pavlic; +Cc: netdev, linux-kernel
In-Reply-To: <OFA34209F3.7B24497C-ONC125707C.0056132C-C125707C.0056326B@de.ibm.com>
On Wed, Sep 14, 2005 at 05:38:26PM +0200, Frank Pavlic wrote:
> Jeff ,
> didn't you get the qeth patches ? let me know if not I will resend them
> once again ....
You only resent patch #2, so please do resend patches 3 and 4.
Thanks,
Jeff
^ permalink raw reply
* Re: [PATCH 1/3] orinoco: WE-18 support
From: Pavel Roskin @ 2005-09-14 15:39 UTC (permalink / raw)
To: Jeff Garzik
Cc: Orinoco Development List, netdev-u79uwXL29TY76Z2rM5mHXA,
Jean Tourrilhes
In-Reply-To: <43281824.7000705-e+AXbWqSrlAAvxtiuMwx3w@public.gmane.org>
On Wed, 2005-09-14 at 08:31 -0400, Jeff Garzik wrote:
> Pavel Roskin wrote:
> > Author: Jean Tourrilhes <jt-sDzT885Ts8HQT0dZR+AlfA@public.gmane.org>
> > Signed-off-by: Pavel Roskin <proski-mXXj517/zsQ@public.gmane.org>
> >
> > Use new Wireless Extension API for wireless stats.
>
> Applied patch #1, the rest failed:
Thanks! The rest will be resubmitted shortly.
--
Regards,
Pavel Roskin
-------------------------------------------------------
SF.Net email is sponsored by:
Tame your development challenges with Apache's Geronimo App Server.
Download it for free - -and be entered to win a 42" plasma tv or your very
own Sony(tm)PSP. Click here to play: http://sourceforge.net/geronimo.php
^ permalink raw reply
* Re: [git patches] net driver fixes
From: Frank Pavlic @ 2005-09-14 15:38 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linux-kernel
In-Reply-To: <20050914131414.GA1095@havoc.gtf.org>
Jeff ,
didn't you get the qeth patches ? let me know if not I will resend them
once again ....
Thanks
linux-kernel-owner@vger.kernel.org wrote on 14.09.2005 15:14:14:
>
> Please pull from 'upstream-fixes' branch of
> rsync://rsync.kernel.org/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
>
> to obtain the following fixes:
>
>
> drivers/net/Kconfig | 2
> drivers/net/e100.c | 4 -
> drivers/net/e1000/e1000_main.c | 1
> drivers/net/ixgb/ixgb_main.c | 2
> drivers/net/s2io.c | 9 ++-
> drivers/net/sk98lin/skge.c | 12 ++---
> drivers/net/skge.c | 98
> ++++++++++++++++++++++-------------------
> drivers/net/skge.h | 2
> drivers/net/tulip/xircom_cb.c | 2
> drivers/net/wireless/airo.c | 5 +-
> drivers/s390/net/ctcmain.c | 41 +++++++++--------
> 11 files changed, 94 insertions(+), 84 deletions(-)
>
>
>
> Andrew Morton:
> s2io warning fixes
>
> Frank Pavlic:
> s390: ctc driver fixes
>
> John W. Linville:
> e1000: correct rx_dropped counting
> e100: correct rx_dropped and add rx_missed_errors
> ixgb: correct rx_dropped counting
>
> Keith Owens:
> Correct xircom_cb use of CONFIG_NET_POLL_CONTROLLER
>
> matthieu castet:
> airo : fix channel number in scan
>
> Stephen Hemminger:
> sk98lin: remove PCI id info for cards for conflicting devices
> skge: gmac register access errors in dual port
>
>
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -1951,7 +1951,7 @@ config SKGE
> ---help---
> This driver support the Marvell Yukon or SysKonnect
SK-98xx/SK-95xx
> and related Gigabit Ethernet adapters. It is a new smaller driver
> - driver with better performance and more complete ethtool support.
> + with better performance and more complete ethtool support.
>
> It does not support the link failover and network management
> features that "portable" vendor supplied sk98lin driver does.
> diff --git a/drivers/net/e100.c b/drivers/net/e100.c
> --- a/drivers/net/e100.c
> +++ b/drivers/net/e100.c
> @@ -1387,13 +1387,13 @@ static void e100_update_stats(struct nic
> ns->collisions += nic->tx_collisions;
> ns->tx_errors += le32_to_cpu(s->tx_max_collisions) +
> le32_to_cpu(s->tx_lost_crs);
> - ns->rx_dropped += le32_to_cpu(s->rx_resource_errors);
> ns->rx_length_errors += le32_to_cpu(s->rx_short_frame_errors) +
> nic->rx_over_length_errors;
> ns->rx_crc_errors += le32_to_cpu(s->rx_crc_errors);
> ns->rx_frame_errors += le32_to_cpu(s->rx_alignment_errors);
> ns->rx_over_errors += le32_to_cpu(s->rx_overrun_errors);
> ns->rx_fifo_errors += le32_to_cpu(s->rx_overrun_errors);
> + ns->rx_missed_errors += le32_to_cpu(s->rx_resource_errors);
> ns->rx_errors += le32_to_cpu(s->rx_crc_errors) +
> le32_to_cpu(s->rx_alignment_errors) +
> le32_to_cpu(s->rx_short_frame_errors) +
> @@ -1727,12 +1727,10 @@ static inline int e100_rx_indicate(struc
>
> if(unlikely(!(rfd_status & cb_ok))) {
> /* Don't indicate if hardware indicates errors */
> - nic->net_stats.rx_dropped++;
> dev_kfree_skb_any(skb);
> } else if(actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) {
> /* Don't indicate oversized frames */
> nic->rx_over_length_errors++;
> - nic->net_stats.rx_dropped++;
> dev_kfree_skb_any(skb);
> } else {
> nic->net_stats.rx_packets++;
> diff --git a/drivers/net/e1000/e1000_main.c
b/drivers/net/e1000/e1000_main.c
> --- a/drivers/net/e1000/e1000_main.c
> +++ b/drivers/net/e1000/e1000_main.c
> @@ -2544,7 +2544,6 @@ e1000_update_stats(struct e1000_adapter
> adapter->stats.crcerrs + adapter->stats.algnerrc +
> adapter->stats.rlec + adapter->stats.mpc +
> adapter->stats.cexterr;
> - adapter->net_stats.rx_dropped = adapter->stats.mpc;
> adapter->net_stats.rx_length_errors = adapter->stats.rlec;
> adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
> adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc;
> diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
> --- a/drivers/net/ixgb/ixgb_main.c
> +++ b/drivers/net/ixgb/ixgb_main.c
> @@ -1616,8 +1616,6 @@ ixgb_update_stats(struct ixgb_adapter *a
> adapter->stats.icbc +
> adapter->stats.ecbc + adapter->stats.mpc;
>
> - adapter->net_stats.rx_dropped = adapter->stats.mpc;
> -
> /* see above
> * adapter->net_stats.rx_length_errors = adapter->stats.rlec;
> */
> diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
> --- a/drivers/net/s2io.c
> +++ b/drivers/net/s2io.c
> @@ -428,7 +428,7 @@ static int init_shared_mem(struct s2io_n
> DBG_PRINT(INIT_DBG,
> "%s: Zero DMA address for TxDL. ", dev->name);
> DBG_PRINT(INIT_DBG,
> - "Virtual address %llx\n", (u64)tmp_v);
> + "Virtual address %p\n", tmp_v);
> tmp_v = pci_alloc_consistent(nic->pdev,
> PAGE_SIZE, &tmp_p);
> if (!tmp_v) {
> @@ -657,9 +657,10 @@ static void free_shared_mem(struct s2io_
> mac_control->zerodma_virt_addr,
> (dma_addr_t)0);
> DBG_PRINT(INIT_DBG,
> - "%s: Freeing TxDL with zero DMA addr. ", dev->name);
> - DBG_PRINT(INIT_DBG, "Virtual address %llx\n",
> - (u64)(mac_control->zerodma_virt_addr));
> + "%s: Freeing TxDL with zero DMA addr. ",
> + dev->name);
> + DBG_PRINT(INIT_DBG, "Virtual address %p\n",
> + mac_control->zerodma_virt_addr);
> }
> kfree(mac_control->fifos[i].list_info);
> }
> diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c
> --- a/drivers/net/sk98lin/skge.c
> +++ b/drivers/net/sk98lin/skge.c
> @@ -5216,17 +5216,15 @@ static struct pci_device_id skge_pci_tbl
> { PCI_VENDOR_ID_3COM, 0x80eb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> { PCI_VENDOR_ID_SYSKONNECT, 0x4300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
},
> { PCI_VENDOR_ID_SYSKONNECT, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
},
> - { PCI_VENDOR_ID_DLINK, 0x4c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> +/* DLink card does not have valid VPD so this driver gags
> + * { PCI_VENDOR_ID_DLINK, 0x4c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> + */
> { PCI_VENDOR_ID_MARVELL, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> -#if 0 /* don't handle Yukon2 cards at the moment --
> mlindner@syskonnect.de */
> - { PCI_VENDOR_ID_MARVELL, 0x4360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> - { PCI_VENDOR_ID_MARVELL, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> -#endif
> { PCI_VENDOR_ID_MARVELL, 0x5005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> { PCI_VENDOR_ID_CNET, 0x434e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> - { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> + { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015, },
> { PCI_VENDOR_ID_LINKSYS, 0x1064, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> - { 0, }
> + { 0 }
> };
>
> MODULE_DEVICE_TABLE(pci, skge_pci_tbl);
> diff --git a/drivers/net/skge.c b/drivers/net/skge.c
> --- a/drivers/net/skge.c
> +++ b/drivers/net/skge.c
> @@ -42,7 +42,7 @@
> #include "skge.h"
>
> #define DRV_NAME "skge"
> -#define DRV_VERSION "0.9"
> +#define DRV_VERSION "1.0"
> #define PFX DRV_NAME " "
>
> #define DEFAULT_TX_RING_SIZE 128
> @@ -669,7 +669,7 @@ static void skge_led(struct skge_port *s
> PHY_M_LED_BLINK_RT(BLINK_84MS) |
> PHY_M_LEDC_TX_CTRL |
> PHY_M_LEDC_DP_CTRL);
> -
> +
> gm_phy_write(hw, port, PHY_MARV_LED_OVER,
> PHY_M_LED_MO_RX(MO_LED_OFF) |
> (skge->speed == SPEED_100 ?
> @@ -876,7 +876,7 @@ static int skge_rx_fill(struct skge_port
>
> static void skge_link_up(struct skge_port *skge)
> {
> - skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
> + skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
> LED_BLK_OFF|LED_SYNC_OFF|LED_ON);
>
> netif_carrier_on(skge->netdev);
> @@ -987,6 +987,8 @@ static void genesis_reset(struct skge_hw
> {
> const u8 zero[8] = { 0 };
>
> + skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
> +
> /* reset the statistics module */
> xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT);
> xm_write16(hw, port, XM_IMSK, 0xffff); /* disable XMAC IRQs */
> @@ -1021,8 +1023,6 @@ static void bcom_check_link(struct skge_
> (void) xm_phy_read(hw, port, PHY_BCOM_STAT);
> status = xm_phy_read(hw, port, PHY_BCOM_STAT);
>
> - pr_debug("bcom_check_link status=0x%x\n", status);
> -
> if ((status & PHY_ST_LSYNC) == 0) {
> u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
> cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
> @@ -1106,8 +1106,6 @@ static void bcom_phy_init(struct skge_po
> { 0x17, 0x0013 }, { 0x15, 0x0A04 }, { 0x18, 0x0420 },
> };
>
> - pr_debug("bcom_phy_init\n");
> -
> /* read Id from external PHY (all have the same address) */
> id1 = xm_phy_read(hw, port, PHY_XMAC_ID1);
>
> @@ -1340,6 +1338,8 @@ static void genesis_stop(struct skge_por
> int port = skge->port;
> u32 reg;
>
> + genesis_reset(hw, port);
> +
> /* Clear Tx packet arbiter timeout IRQ */
> skge_write16(hw, B3_PA_CTRL,
> port == 0 ? PA_CLR_TO_TX1 : PA_CLR_TO_TX2);
> @@ -1465,7 +1465,6 @@ static void genesis_link_up(struct skge_
> u16 cmd;
> u32 mode, msk;
>
> - pr_debug("genesis_link_up\n");
> cmd = xm_read16(hw, port, XM_MMU_CMD);
>
> /*
> @@ -1578,7 +1577,6 @@ static void yukon_init(struct skge_hw *h
> struct skge_port *skge = netdev_priv(hw->dev[port]);
> u16 ctrl, ct1000, adv;
>
> - pr_debug("yukon_init\n");
> if (skge->autoneg == AUTONEG_ENABLE) {
> u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
>
> @@ -1677,9 +1675,11 @@ static void yukon_mac_init(struct skge_h
>
> /* WA code for COMA mode -- set PHY reset */
> if (hw->chip_id == CHIP_ID_YUKON_LITE &&
> - hw->chip_rev >= CHIP_REV_YU_LITE_A3)
> - skge_write32(hw, B2_GP_IO,
> - (skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9));
> + hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
> + reg = skge_read32(hw, B2_GP_IO);
> + reg |= GP_DIR_9 | GP_IO_9;
> + skge_write32(hw, B2_GP_IO, reg);
> + }
>
> /* hard reset */
> skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
> @@ -1687,10 +1687,12 @@ static void yukon_mac_init(struct skge_h
>
> /* WA code for COMA mode -- clear PHY reset */
> if (hw->chip_id == CHIP_ID_YUKON_LITE &&
> - hw->chip_rev >= CHIP_REV_YU_LITE_A3)
> - skge_write32(hw, B2_GP_IO,
> - (skge_read32(hw, B2_GP_IO) | GP_DIR_9)
> - & ~GP_IO_9);
> + hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
> + reg = skge_read32(hw, B2_GP_IO);
> + reg |= GP_DIR_9;
> + reg &= ~GP_IO_9;
> + skge_write32(hw, B2_GP_IO, reg);
> + }
>
> /* Set hardware config mode */
> reg = GPC_INT_POL_HI | GPC_DIS_FC | GPC_DIS_SLEEP |
> @@ -1729,7 +1731,7 @@ static void yukon_mac_init(struct skge_h
> }
>
> gma_write16(hw, port, GM_GP_CTRL, reg);
> - skge_read16(hw, GMAC_IRQ_SRC);
> + skge_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
>
> yukon_init(hw, port);
>
> @@ -1801,20 +1803,26 @@ static void yukon_stop(struct skge_port
> struct skge_hw *hw = skge->hw;
> int port = skge->port;
>
> - if (hw->chip_id == CHIP_ID_YUKON_LITE &&
> - hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
> - skge_write32(hw, B2_GP_IO,
> - skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9);
> - }
> + skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
> + yukon_reset(hw, port);
>
> gma_write16(hw, port, GM_GP_CTRL,
> gma_read16(hw, port, GM_GP_CTRL)
> & ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA));
> gma_read16(hw, port, GM_GP_CTRL);
>
> + if (hw->chip_id == CHIP_ID_YUKON_LITE &&
> + hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
> + u32 io = skge_read32(hw, B2_GP_IO);
> +
> + io |= GP_DIR_9 | GP_IO_9;
> + skge_write32(hw, B2_GP_IO, io);
> + skge_read32(hw, B2_GP_IO);
> + }
> +
> /* set GPHY Control reset */
> - skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
> - skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
> + skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
> + skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
> }
>
> static void yukon_get_stats(struct skge_port *skge, u64 *data)
> @@ -1873,10 +1881,8 @@ static void yukon_link_up(struct skge_po
> int port = skge->port;
> u16 reg;
>
> - pr_debug("yukon_link_up\n");
> -
> /* Enable Transmit FIFO Underrun */
> - skge_write8(hw, GMAC_IRQ_MSK, GMAC_DEF_MSK);
> + skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
>
> reg = gma_read16(hw, port, GM_GP_CTRL);
> if (skge->duplex == DUPLEX_FULL || skge->autoneg == AUTONEG_ENABLE)
> @@ -1896,7 +1902,6 @@ static void yukon_link_down(struct skge_
> int port = skge->port;
> u16 ctrl;
>
> - pr_debug("yukon_link_down\n");
> gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
>
> ctrl = gma_read16(hw, port, GM_GP_CTRL);
> @@ -2112,7 +2117,6 @@ static int skge_up(struct net_device *de
> skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START |
CSR_IRQ_CL_F);
> skge_led(skge, LED_MODE_ON);
>
> - pr_debug("skge_up completed\n");
> return 0;
>
> free_rx_ring:
> @@ -2135,15 +2139,20 @@ static int skge_down(struct net_device *
>
> netif_stop_queue(dev);
>
> + skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
> + if (hw->chip_id == CHIP_ID_GENESIS)
> + genesis_stop(skge);
> + else
> + yukon_stop(skge);
> +
> + hw->intr_mask &= ~portirqmask[skge->port];
> + skge_write32(hw, B0_IMSK, hw->intr_mask);
> +
> /* Stop transmitter */
> skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP);
> skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
> RB_RST_SET|RB_DIS_OP_MD);
>
> - if (hw->chip_id == CHIP_ID_GENESIS)
> - genesis_stop(skge);
> - else
> - yukon_stop(skge);
>
> /* Disable Force Sync bit and Enable Alloc bit */
> skge_write8(hw, SK_REG(port, TXA_CTRL),
> @@ -2367,8 +2376,6 @@ static void genesis_set_multicast(struct
> u32 mode;
> u8 filter[8];
>
> - pr_debug("genesis_set_multicast flags=%x count=%d\n",
> dev->flags, dev->mc_count);
> -
> mode = xm_read32(hw, port, XM_MODE);
> mode |= XM_MD_ENA_HASH;
> if (dev->flags & IFF_PROMISC)
> @@ -2530,8 +2537,6 @@ static int skge_poll(struct net_device *
> unsigned int to_do = min(dev->quota, *budget);
> unsigned int work_done = 0;
>
> - pr_debug("skge_poll\n");
> -
> for (e = ring->to_clean; work_done < to_do; e = e->next) {
> struct skge_rx_desc *rd = e->desc;
> struct sk_buff *skb;
> @@ -2672,9 +2677,9 @@ static void skge_error_irq(struct skge_h
> if (hw->chip_id == CHIP_ID_GENESIS) {
> /* clear xmac errors */
> if (hwstatus & (IS_NO_STAT_M1|IS_NO_TIST_M1))
> - skge_write16(hw, SK_REG(0, RX_MFF_CTRL1), MFF_CLR_INSTAT);
> + skge_write16(hw, RX_MFF_CTRL1, MFF_CLR_INSTAT);
> if (hwstatus & (IS_NO_STAT_M2|IS_NO_TIST_M2))
> - skge_write16(hw, SK_REG(0, RX_MFF_CTRL2), MFF_CLR_INSTAT);
> + skge_write16(hw, RX_MFF_CTRL2, MFF_CLR_INSTAT);
> } else {
> /* Timestamp (unused) overflow */
> if (hwstatus & IS_IRQ_TIST_OV)
> @@ -3000,9 +3005,6 @@ static int skge_reset(struct skge_hw *hw
>
> skge_write32(hw, B0_IMSK, hw->intr_mask);
>
> - if (hw->chip_id != CHIP_ID_GENESIS)
> - skge_write8(hw, GMAC_IRQ_MSK, 0);
> -
> spin_lock_bh(&hw->phy_lock);
> for (i = 0; i < hw->ports; i++) {
> if (hw->chip_id == CHIP_ID_GENESIS)
> @@ -3230,6 +3232,11 @@ static void __devexit skge_remove(struct
> dev0 = hw->dev[0];
> unregister_netdev(dev0);
>
> + skge_write32(hw, B0_IMSK, 0);
> + skge_write16(hw, B0_LED, LED_STAT_OFF);
> + skge_pci_clear(hw);
> + skge_write8(hw, B0_CTST, CS_RST_SET);
> +
> tasklet_kill(&hw->ext_tasklet);
>
> free_irq(pdev->irq, hw);
> @@ -3238,7 +3245,7 @@ static void __devexit skge_remove(struct
> if (dev1)
> free_netdev(dev1);
> free_netdev(dev0);
> - skge_write16(hw, B0_LED, LED_STAT_OFF);
> +
> iounmap(hw->regs);
> kfree(hw);
> pci_set_drvdata(pdev, NULL);
> @@ -3257,7 +3264,10 @@ static int skge_suspend(struct pci_dev *
> struct skge_port *skge = netdev_priv(dev);
> if (netif_running(dev)) {
> netif_carrier_off(dev);
> - skge_down(dev);
> + if (skge->wol)
> + netif_stop_queue(dev);
> + else
> + skge_down(dev);
> }
> netif_device_detach(dev);
> wol |= skge->wol;
> diff --git a/drivers/net/skge.h b/drivers/net/skge.h
> --- a/drivers/net/skge.h
> +++ b/drivers/net/skge.h
> @@ -2008,7 +2008,7 @@ enum {
> GM_IS_RX_FF_OR = 1<<1, /* Receive FIFO Overrun */
> GM_IS_RX_COMPL = 1<<0, /* Frame Reception Complete */
>
> -#define GMAC_DEF_MSK (GM_IS_TX_CO_OV | GM_IS_RX_CO_OV |
GM_IS_TX_FF_UR)
> +#define GMAC_DEF_MSK (GM_IS_RX_FF_OR | GM_IS_TX_FF_UR)
>
> /* GMAC_LINK_CTRL 16 bit GMAC Link Control Reg (YUKON only) */
> /* Bits 15.. 2: reserved */
> diff --git a/drivers/net/tulip/xircom_cb.c
b/drivers/net/tulip/xircom_cb.c
> --- a/drivers/net/tulip/xircom_cb.c
> +++ b/drivers/net/tulip/xircom_cb.c
> @@ -117,7 +117,7 @@ static int xircom_open(struct net_device
> static int xircom_close(struct net_device *dev);
> static void xircom_up(struct xircom_private *card);
> static struct net_device_stats *xircom_get_stats(struct net_device
*dev);
> -#if CONFIG_NET_POLL_CONTROLLER
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> static void xircom_poll_controller(struct net_device *dev);
> #endif
>
> diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
> --- a/drivers/net/wireless/airo.c
> +++ b/drivers/net/wireless/airo.c
> @@ -6852,7 +6852,10 @@ static inline char *airo_translate_scan(
> /* Add frequency */
> iwe.cmd = SIOCGIWFREQ;
> iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
> - iwe.u.freq.m = frequency_list[iwe.u.freq.m] * 100000;
> + /* iwe.u.freq.m containt the channel (starting 1), our
> + * frequency_list array start at index 0...
> + */
> + iwe.u.freq.m = frequency_list[iwe.u.freq.m - 1] * 100000;
> iwe.u.freq.e = 1;
> current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
> IW_EV_FREQ_LEN);
>
> diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
> --- a/drivers/s390/net/ctcmain.c
> +++ b/drivers/s390/net/ctcmain.c
> @@ -1,5 +1,5 @@
> /*
> - * $Id: ctcmain.c,v 1.74 2005/03/24 09:04:17 mschwide Exp $
> + * $Id: ctcmain.c,v 1.78 2005/09/07 12:18:02 pavlic Exp $
> *
> * CTC / ESCON network driver
> *
> @@ -37,10 +37,9 @@
> * along with this program; if not, write to the Free Software
> * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> *
> - * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.74 $
> + * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.78 $
> *
> */
> -\f
> #undef DEBUG
> #include <linux/module.h>
> #include <linux/init.h>
> @@ -135,7 +134,7 @@ static const char *dev_event_names[] = {
> "TX down",
> "Restart",
> };
> -\f
> +
> /**
> * Events of the channel statemachine
> */
> @@ -249,7 +248,7 @@ static void
> print_banner(void)
> {
> static int printed = 0;
> - char vbuf[] = "$Revision: 1.74 $";
> + char vbuf[] = "$Revision: 1.78 $";
> char *version = vbuf;
>
> if (printed)
> @@ -334,7 +333,7 @@ static const char *ch_state_names[] = {
> "Restarting",
> "Not operational",
> };
> -\f
> +
> #ifdef DEBUG
> /**
> * Dump header and first 16 bytes of an sk_buff for debugging purposes.
> @@ -671,7 +670,7 @@ static void
> fsm_action_nop(fsm_instance * fi, int event, void *arg)
> {
> }
> -\f
> +
> /**
> * Actions for channel - statemachines.
>
>
*****************************************************************************/
> @@ -1514,7 +1513,6 @@ ch_action_reinit(fsm_instance *fi, int e
> fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART,
dev);
> }
>
> -\f
> /**
> * The statemachine for a channel.
> */
> @@ -1625,7 +1623,7 @@ static const fsm_node ch_fsm[] = {
> };
>
> static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node);
> -\f
> +
> /**
> * Functions related to setup and device detection.
>
>
*****************************************************************************/
> @@ -1976,7 +1974,7 @@ ctc_irq_handler(struct ccw_device *cdev,
> fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
>
> }
> -\f
> +
> /**
> * Actions for interface - statemachine.
>
>
*****************************************************************************/
> @@ -2209,13 +2207,18 @@ transmit_skb(struct channel *ch, struct
> int rc = 0;
>
> DBF_TEXT(trace, 5, __FUNCTION__);
> + /* we need to acquire the lock for testing the state
> + * otherwise we can have an IRQ changing the state to
> + * TXIDLE after the test but before acquiring the lock.
> + */
> + spin_lock_irqsave(&ch->collect_lock, saveflags);
> if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) {
> int l = skb->len + LL_HEADER_LENGTH;
>
> - spin_lock_irqsave(&ch->collect_lock, saveflags);
> - if (ch->collect_len + l > ch->max_bufsize - 2)
> - rc = -EBUSY;
> - else {
> + if (ch->collect_len + l > ch->max_bufsize - 2) {
> + spin_unlock_irqrestore(&ch->collect_lock, saveflags);
> + return -EBUSY;
> + } else {
> atomic_inc(&skb->users);
> header.length = l;
> header.type = skb->protocol;
> @@ -2231,7 +2234,7 @@ transmit_skb(struct channel *ch, struct
> int ccw_idx;
> struct sk_buff *nskb;
> unsigned long hi;
> -
> + spin_unlock_irqrestore(&ch->collect_lock, saveflags);
> /**
> * Protect skb against beeing free'd by upper
> * layers.
> @@ -2256,6 +2259,7 @@ transmit_skb(struct channel *ch, struct
> if (!nskb) {
> atomic_dec(&skb->users);
> skb_pull(skb, LL_HEADER_LENGTH + 2);
> + ctc_clear_busy(ch->netdev);
> return -ENOMEM;
> } else {
> memcpy(skb_put(nskb, skb->len),
> @@ -2281,6 +2285,7 @@ transmit_skb(struct channel *ch, struct
> */
> atomic_dec(&skb->users);
> skb_pull(skb, LL_HEADER_LENGTH + 2);
> + ctc_clear_busy(ch->netdev);
> return -EBUSY;
> }
>
> @@ -2327,9 +2332,10 @@ transmit_skb(struct channel *ch, struct
> }
> }
>
> + ctc_clear_busy(ch->netdev);
> return rc;
> }
> -\f
> +
> /**
> * Interface API for upper network layers
>
>
*****************************************************************************/
> @@ -2421,7 +2427,6 @@ ctc_tx(struct sk_buff *skb, struct net_d
> dev->trans_start = jiffies;
> if (transmit_skb(privptr->channel[WRITE], skb) != 0)
> rc = 1;
> - ctc_clear_busy(dev);
> return rc;
> }
>
> @@ -2610,7 +2615,6 @@ stats_write(struct device *dev, struct d
> return count;
> }
>
> -\f
> static void
> ctc_netdev_unregister(struct net_device * dev)
> {
> @@ -2685,7 +2689,6 @@ ctc_proto_store(struct device *dev, stru
> return count;
> }
>
> -
> static ssize_t
> ctc_type_show(struct device *dev, struct device_attribute *attr, char
*buf)
> {
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* [git patches] net driver fixes
From: Jeff Garzik @ 2005-09-14 13:14 UTC (permalink / raw)
To: Andrew Morton, Linus Torvalds; +Cc: netdev, linux-kernel
Please pull from 'upstream-fixes' branch of
rsync://rsync.kernel.org/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
to obtain the following fixes:
drivers/net/Kconfig | 2
drivers/net/e100.c | 4 -
drivers/net/e1000/e1000_main.c | 1
drivers/net/ixgb/ixgb_main.c | 2
drivers/net/s2io.c | 9 ++-
drivers/net/sk98lin/skge.c | 12 ++---
drivers/net/skge.c | 98 ++++++++++++++++++++++-------------------
drivers/net/skge.h | 2
drivers/net/tulip/xircom_cb.c | 2
drivers/net/wireless/airo.c | 5 +-
drivers/s390/net/ctcmain.c | 41 +++++++++--------
11 files changed, 94 insertions(+), 84 deletions(-)
Andrew Morton:
s2io warning fixes
Frank Pavlic:
s390: ctc driver fixes
John W. Linville:
e1000: correct rx_dropped counting
e100: correct rx_dropped and add rx_missed_errors
ixgb: correct rx_dropped counting
Keith Owens:
Correct xircom_cb use of CONFIG_NET_POLL_CONTROLLER
matthieu castet:
airo : fix channel number in scan
Stephen Hemminger:
sk98lin: remove PCI id info for cards for conflicting devices
skge: gmac register access errors in dual port
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1951,7 +1951,7 @@ config SKGE
---help---
This driver support the Marvell Yukon or SysKonnect SK-98xx/SK-95xx
and related Gigabit Ethernet adapters. It is a new smaller driver
- driver with better performance and more complete ethtool support.
+ with better performance and more complete ethtool support.
It does not support the link failover and network management
features that "portable" vendor supplied sk98lin driver does.
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1387,13 +1387,13 @@ static void e100_update_stats(struct nic
ns->collisions += nic->tx_collisions;
ns->tx_errors += le32_to_cpu(s->tx_max_collisions) +
le32_to_cpu(s->tx_lost_crs);
- ns->rx_dropped += le32_to_cpu(s->rx_resource_errors);
ns->rx_length_errors += le32_to_cpu(s->rx_short_frame_errors) +
nic->rx_over_length_errors;
ns->rx_crc_errors += le32_to_cpu(s->rx_crc_errors);
ns->rx_frame_errors += le32_to_cpu(s->rx_alignment_errors);
ns->rx_over_errors += le32_to_cpu(s->rx_overrun_errors);
ns->rx_fifo_errors += le32_to_cpu(s->rx_overrun_errors);
+ ns->rx_missed_errors += le32_to_cpu(s->rx_resource_errors);
ns->rx_errors += le32_to_cpu(s->rx_crc_errors) +
le32_to_cpu(s->rx_alignment_errors) +
le32_to_cpu(s->rx_short_frame_errors) +
@@ -1727,12 +1727,10 @@ static inline int e100_rx_indicate(struc
if(unlikely(!(rfd_status & cb_ok))) {
/* Don't indicate if hardware indicates errors */
- nic->net_stats.rx_dropped++;
dev_kfree_skb_any(skb);
} else if(actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) {
/* Don't indicate oversized frames */
nic->rx_over_length_errors++;
- nic->net_stats.rx_dropped++;
dev_kfree_skb_any(skb);
} else {
nic->net_stats.rx_packets++;
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -2544,7 +2544,6 @@ e1000_update_stats(struct e1000_adapter
adapter->stats.crcerrs + adapter->stats.algnerrc +
adapter->stats.rlec + adapter->stats.mpc +
adapter->stats.cexterr;
- adapter->net_stats.rx_dropped = adapter->stats.mpc;
adapter->net_stats.rx_length_errors = adapter->stats.rlec;
adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc;
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -1616,8 +1616,6 @@ ixgb_update_stats(struct ixgb_adapter *a
adapter->stats.icbc +
adapter->stats.ecbc + adapter->stats.mpc;
- adapter->net_stats.rx_dropped = adapter->stats.mpc;
-
/* see above
* adapter->net_stats.rx_length_errors = adapter->stats.rlec;
*/
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -428,7 +428,7 @@ static int init_shared_mem(struct s2io_n
DBG_PRINT(INIT_DBG,
"%s: Zero DMA address for TxDL. ", dev->name);
DBG_PRINT(INIT_DBG,
- "Virtual address %llx\n", (u64)tmp_v);
+ "Virtual address %p\n", tmp_v);
tmp_v = pci_alloc_consistent(nic->pdev,
PAGE_SIZE, &tmp_p);
if (!tmp_v) {
@@ -657,9 +657,10 @@ static void free_shared_mem(struct s2io_
mac_control->zerodma_virt_addr,
(dma_addr_t)0);
DBG_PRINT(INIT_DBG,
- "%s: Freeing TxDL with zero DMA addr. ", dev->name);
- DBG_PRINT(INIT_DBG, "Virtual address %llx\n",
- (u64)(mac_control->zerodma_virt_addr));
+ "%s: Freeing TxDL with zero DMA addr. ",
+ dev->name);
+ DBG_PRINT(INIT_DBG, "Virtual address %p\n",
+ mac_control->zerodma_virt_addr);
}
kfree(mac_control->fifos[i].list_info);
}
diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c
--- a/drivers/net/sk98lin/skge.c
+++ b/drivers/net/sk98lin/skge.c
@@ -5216,17 +5216,15 @@ static struct pci_device_id skge_pci_tbl
{ PCI_VENDOR_ID_3COM, 0x80eb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_SYSKONNECT, 0x4300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_SYSKONNECT, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VENDOR_ID_DLINK, 0x4c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+/* DLink card does not have valid VPD so this driver gags
+ * { PCI_VENDOR_ID_DLINK, 0x4c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ */
{ PCI_VENDOR_ID_MARVELL, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-#if 0 /* don't handle Yukon2 cards at the moment -- mlindner@syskonnect.de */
- { PCI_VENDOR_ID_MARVELL, 0x4360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VENDOR_ID_MARVELL, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-#endif
{ PCI_VENDOR_ID_MARVELL, 0x5005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_CNET, 0x434e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015, },
{ PCI_VENDOR_ID_LINKSYS, 0x1064, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { 0, }
+ { 0 }
};
MODULE_DEVICE_TABLE(pci, skge_pci_tbl);
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -42,7 +42,7 @@
#include "skge.h"
#define DRV_NAME "skge"
-#define DRV_VERSION "0.9"
+#define DRV_VERSION "1.0"
#define PFX DRV_NAME " "
#define DEFAULT_TX_RING_SIZE 128
@@ -669,7 +669,7 @@ static void skge_led(struct skge_port *s
PHY_M_LED_BLINK_RT(BLINK_84MS) |
PHY_M_LEDC_TX_CTRL |
PHY_M_LEDC_DP_CTRL);
-
+
gm_phy_write(hw, port, PHY_MARV_LED_OVER,
PHY_M_LED_MO_RX(MO_LED_OFF) |
(skge->speed == SPEED_100 ?
@@ -876,7 +876,7 @@ static int skge_rx_fill(struct skge_port
static void skge_link_up(struct skge_port *skge)
{
- skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
+ skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
LED_BLK_OFF|LED_SYNC_OFF|LED_ON);
netif_carrier_on(skge->netdev);
@@ -987,6 +987,8 @@ static void genesis_reset(struct skge_hw
{
const u8 zero[8] = { 0 };
+ skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+
/* reset the statistics module */
xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT);
xm_write16(hw, port, XM_IMSK, 0xffff); /* disable XMAC IRQs */
@@ -1021,8 +1023,6 @@ static void bcom_check_link(struct skge_
(void) xm_phy_read(hw, port, PHY_BCOM_STAT);
status = xm_phy_read(hw, port, PHY_BCOM_STAT);
- pr_debug("bcom_check_link status=0x%x\n", status);
-
if ((status & PHY_ST_LSYNC) == 0) {
u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
@@ -1106,8 +1106,6 @@ static void bcom_phy_init(struct skge_po
{ 0x17, 0x0013 }, { 0x15, 0x0A04 }, { 0x18, 0x0420 },
};
- pr_debug("bcom_phy_init\n");
-
/* read Id from external PHY (all have the same address) */
id1 = xm_phy_read(hw, port, PHY_XMAC_ID1);
@@ -1340,6 +1338,8 @@ static void genesis_stop(struct skge_por
int port = skge->port;
u32 reg;
+ genesis_reset(hw, port);
+
/* Clear Tx packet arbiter timeout IRQ */
skge_write16(hw, B3_PA_CTRL,
port == 0 ? PA_CLR_TO_TX1 : PA_CLR_TO_TX2);
@@ -1465,7 +1465,6 @@ static void genesis_link_up(struct skge_
u16 cmd;
u32 mode, msk;
- pr_debug("genesis_link_up\n");
cmd = xm_read16(hw, port, XM_MMU_CMD);
/*
@@ -1578,7 +1577,6 @@ static void yukon_init(struct skge_hw *h
struct skge_port *skge = netdev_priv(hw->dev[port]);
u16 ctrl, ct1000, adv;
- pr_debug("yukon_init\n");
if (skge->autoneg == AUTONEG_ENABLE) {
u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
@@ -1677,9 +1675,11 @@ static void yukon_mac_init(struct skge_h
/* WA code for COMA mode -- set PHY reset */
if (hw->chip_id == CHIP_ID_YUKON_LITE &&
- hw->chip_rev >= CHIP_REV_YU_LITE_A3)
- skge_write32(hw, B2_GP_IO,
- (skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9));
+ hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+ reg = skge_read32(hw, B2_GP_IO);
+ reg |= GP_DIR_9 | GP_IO_9;
+ skge_write32(hw, B2_GP_IO, reg);
+ }
/* hard reset */
skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
@@ -1687,10 +1687,12 @@ static void yukon_mac_init(struct skge_h
/* WA code for COMA mode -- clear PHY reset */
if (hw->chip_id == CHIP_ID_YUKON_LITE &&
- hw->chip_rev >= CHIP_REV_YU_LITE_A3)
- skge_write32(hw, B2_GP_IO,
- (skge_read32(hw, B2_GP_IO) | GP_DIR_9)
- & ~GP_IO_9);
+ hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+ reg = skge_read32(hw, B2_GP_IO);
+ reg |= GP_DIR_9;
+ reg &= ~GP_IO_9;
+ skge_write32(hw, B2_GP_IO, reg);
+ }
/* Set hardware config mode */
reg = GPC_INT_POL_HI | GPC_DIS_FC | GPC_DIS_SLEEP |
@@ -1729,7 +1731,7 @@ static void yukon_mac_init(struct skge_h
}
gma_write16(hw, port, GM_GP_CTRL, reg);
- skge_read16(hw, GMAC_IRQ_SRC);
+ skge_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
yukon_init(hw, port);
@@ -1801,20 +1803,26 @@ static void yukon_stop(struct skge_port
struct skge_hw *hw = skge->hw;
int port = skge->port;
- if (hw->chip_id == CHIP_ID_YUKON_LITE &&
- hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
- skge_write32(hw, B2_GP_IO,
- skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9);
- }
+ skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+ yukon_reset(hw, port);
gma_write16(hw, port, GM_GP_CTRL,
gma_read16(hw, port, GM_GP_CTRL)
& ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA));
gma_read16(hw, port, GM_GP_CTRL);
+ if (hw->chip_id == CHIP_ID_YUKON_LITE &&
+ hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+ u32 io = skge_read32(hw, B2_GP_IO);
+
+ io |= GP_DIR_9 | GP_IO_9;
+ skge_write32(hw, B2_GP_IO, io);
+ skge_read32(hw, B2_GP_IO);
+ }
+
/* set GPHY Control reset */
- skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
- skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
+ skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+ skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
}
static void yukon_get_stats(struct skge_port *skge, u64 *data)
@@ -1873,10 +1881,8 @@ static void yukon_link_up(struct skge_po
int port = skge->port;
u16 reg;
- pr_debug("yukon_link_up\n");
-
/* Enable Transmit FIFO Underrun */
- skge_write8(hw, GMAC_IRQ_MSK, GMAC_DEF_MSK);
+ skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
reg = gma_read16(hw, port, GM_GP_CTRL);
if (skge->duplex == DUPLEX_FULL || skge->autoneg == AUTONEG_ENABLE)
@@ -1896,7 +1902,6 @@ static void yukon_link_down(struct skge_
int port = skge->port;
u16 ctrl;
- pr_debug("yukon_link_down\n");
gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
ctrl = gma_read16(hw, port, GM_GP_CTRL);
@@ -2112,7 +2117,6 @@ static int skge_up(struct net_device *de
skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F);
skge_led(skge, LED_MODE_ON);
- pr_debug("skge_up completed\n");
return 0;
free_rx_ring:
@@ -2135,15 +2139,20 @@ static int skge_down(struct net_device *
netif_stop_queue(dev);
+ skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
+ if (hw->chip_id == CHIP_ID_GENESIS)
+ genesis_stop(skge);
+ else
+ yukon_stop(skge);
+
+ hw->intr_mask &= ~portirqmask[skge->port];
+ skge_write32(hw, B0_IMSK, hw->intr_mask);
+
/* Stop transmitter */
skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP);
skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
RB_RST_SET|RB_DIS_OP_MD);
- if (hw->chip_id == CHIP_ID_GENESIS)
- genesis_stop(skge);
- else
- yukon_stop(skge);
/* Disable Force Sync bit and Enable Alloc bit */
skge_write8(hw, SK_REG(port, TXA_CTRL),
@@ -2367,8 +2376,6 @@ static void genesis_set_multicast(struct
u32 mode;
u8 filter[8];
- pr_debug("genesis_set_multicast flags=%x count=%d\n", dev->flags, dev->mc_count);
-
mode = xm_read32(hw, port, XM_MODE);
mode |= XM_MD_ENA_HASH;
if (dev->flags & IFF_PROMISC)
@@ -2530,8 +2537,6 @@ static int skge_poll(struct net_device *
unsigned int to_do = min(dev->quota, *budget);
unsigned int work_done = 0;
- pr_debug("skge_poll\n");
-
for (e = ring->to_clean; work_done < to_do; e = e->next) {
struct skge_rx_desc *rd = e->desc;
struct sk_buff *skb;
@@ -2672,9 +2677,9 @@ static void skge_error_irq(struct skge_h
if (hw->chip_id == CHIP_ID_GENESIS) {
/* clear xmac errors */
if (hwstatus & (IS_NO_STAT_M1|IS_NO_TIST_M1))
- skge_write16(hw, SK_REG(0, RX_MFF_CTRL1), MFF_CLR_INSTAT);
+ skge_write16(hw, RX_MFF_CTRL1, MFF_CLR_INSTAT);
if (hwstatus & (IS_NO_STAT_M2|IS_NO_TIST_M2))
- skge_write16(hw, SK_REG(0, RX_MFF_CTRL2), MFF_CLR_INSTAT);
+ skge_write16(hw, RX_MFF_CTRL2, MFF_CLR_INSTAT);
} else {
/* Timestamp (unused) overflow */
if (hwstatus & IS_IRQ_TIST_OV)
@@ -3000,9 +3005,6 @@ static int skge_reset(struct skge_hw *hw
skge_write32(hw, B0_IMSK, hw->intr_mask);
- if (hw->chip_id != CHIP_ID_GENESIS)
- skge_write8(hw, GMAC_IRQ_MSK, 0);
-
spin_lock_bh(&hw->phy_lock);
for (i = 0; i < hw->ports; i++) {
if (hw->chip_id == CHIP_ID_GENESIS)
@@ -3230,6 +3232,11 @@ static void __devexit skge_remove(struct
dev0 = hw->dev[0];
unregister_netdev(dev0);
+ skge_write32(hw, B0_IMSK, 0);
+ skge_write16(hw, B0_LED, LED_STAT_OFF);
+ skge_pci_clear(hw);
+ skge_write8(hw, B0_CTST, CS_RST_SET);
+
tasklet_kill(&hw->ext_tasklet);
free_irq(pdev->irq, hw);
@@ -3238,7 +3245,7 @@ static void __devexit skge_remove(struct
if (dev1)
free_netdev(dev1);
free_netdev(dev0);
- skge_write16(hw, B0_LED, LED_STAT_OFF);
+
iounmap(hw->regs);
kfree(hw);
pci_set_drvdata(pdev, NULL);
@@ -3257,7 +3264,10 @@ static int skge_suspend(struct pci_dev *
struct skge_port *skge = netdev_priv(dev);
if (netif_running(dev)) {
netif_carrier_off(dev);
- skge_down(dev);
+ if (skge->wol)
+ netif_stop_queue(dev);
+ else
+ skge_down(dev);
}
netif_device_detach(dev);
wol |= skge->wol;
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -2008,7 +2008,7 @@ enum {
GM_IS_RX_FF_OR = 1<<1, /* Receive FIFO Overrun */
GM_IS_RX_COMPL = 1<<0, /* Frame Reception Complete */
-#define GMAC_DEF_MSK (GM_IS_TX_CO_OV | GM_IS_RX_CO_OV | GM_IS_TX_FF_UR)
+#define GMAC_DEF_MSK (GM_IS_RX_FF_OR | GM_IS_TX_FF_UR)
/* GMAC_LINK_CTRL 16 bit GMAC Link Control Reg (YUKON only) */
/* Bits 15.. 2: reserved */
diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c
--- a/drivers/net/tulip/xircom_cb.c
+++ b/drivers/net/tulip/xircom_cb.c
@@ -117,7 +117,7 @@ static int xircom_open(struct net_device
static int xircom_close(struct net_device *dev);
static void xircom_up(struct xircom_private *card);
static struct net_device_stats *xircom_get_stats(struct net_device *dev);
-#if CONFIG_NET_POLL_CONTROLLER
+#ifdef CONFIG_NET_POLL_CONTROLLER
static void xircom_poll_controller(struct net_device *dev);
#endif
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -6852,7 +6852,10 @@ static inline char *airo_translate_scan(
/* Add frequency */
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
- iwe.u.freq.m = frequency_list[iwe.u.freq.m] * 100000;
+ /* iwe.u.freq.m containt the channel (starting 1), our
+ * frequency_list array start at index 0...
+ */
+ iwe.u.freq.m = frequency_list[iwe.u.freq.m - 1] * 100000;
iwe.u.freq.e = 1;
current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -1,5 +1,5 @@
/*
- * $Id: ctcmain.c,v 1.74 2005/03/24 09:04:17 mschwide Exp $
+ * $Id: ctcmain.c,v 1.78 2005/09/07 12:18:02 pavlic Exp $
*
* CTC / ESCON network driver
*
@@ -37,10 +37,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.74 $
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.78 $
*
*/
-\f
#undef DEBUG
#include <linux/module.h>
#include <linux/init.h>
@@ -135,7 +134,7 @@ static const char *dev_event_names[] = {
"TX down",
"Restart",
};
-\f
+
/**
* Events of the channel statemachine
*/
@@ -249,7 +248,7 @@ static void
print_banner(void)
{
static int printed = 0;
- char vbuf[] = "$Revision: 1.74 $";
+ char vbuf[] = "$Revision: 1.78 $";
char *version = vbuf;
if (printed)
@@ -334,7 +333,7 @@ static const char *ch_state_names[] = {
"Restarting",
"Not operational",
};
-\f
+
#ifdef DEBUG
/**
* Dump header and first 16 bytes of an sk_buff for debugging purposes.
@@ -671,7 +670,7 @@ static void
fsm_action_nop(fsm_instance * fi, int event, void *arg)
{
}
-\f
+
/**
* Actions for channel - statemachines.
*****************************************************************************/
@@ -1514,7 +1513,6 @@ ch_action_reinit(fsm_instance *fi, int e
fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, dev);
}
-\f
/**
* The statemachine for a channel.
*/
@@ -1625,7 +1623,7 @@ static const fsm_node ch_fsm[] = {
};
static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node);
-\f
+
/**
* Functions related to setup and device detection.
*****************************************************************************/
@@ -1976,7 +1974,7 @@ ctc_irq_handler(struct ccw_device *cdev,
fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
}
-\f
+
/**
* Actions for interface - statemachine.
*****************************************************************************/
@@ -2209,13 +2207,18 @@ transmit_skb(struct channel *ch, struct
int rc = 0;
DBF_TEXT(trace, 5, __FUNCTION__);
+ /* we need to acquire the lock for testing the state
+ * otherwise we can have an IRQ changing the state to
+ * TXIDLE after the test but before acquiring the lock.
+ */
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) {
int l = skb->len + LL_HEADER_LENGTH;
- spin_lock_irqsave(&ch->collect_lock, saveflags);
- if (ch->collect_len + l > ch->max_bufsize - 2)
- rc = -EBUSY;
- else {
+ if (ch->collect_len + l > ch->max_bufsize - 2) {
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ return -EBUSY;
+ } else {
atomic_inc(&skb->users);
header.length = l;
header.type = skb->protocol;
@@ -2231,7 +2234,7 @@ transmit_skb(struct channel *ch, struct
int ccw_idx;
struct sk_buff *nskb;
unsigned long hi;
-
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
/**
* Protect skb against beeing free'd by upper
* layers.
@@ -2256,6 +2259,7 @@ transmit_skb(struct channel *ch, struct
if (!nskb) {
atomic_dec(&skb->users);
skb_pull(skb, LL_HEADER_LENGTH + 2);
+ ctc_clear_busy(ch->netdev);
return -ENOMEM;
} else {
memcpy(skb_put(nskb, skb->len),
@@ -2281,6 +2285,7 @@ transmit_skb(struct channel *ch, struct
*/
atomic_dec(&skb->users);
skb_pull(skb, LL_HEADER_LENGTH + 2);
+ ctc_clear_busy(ch->netdev);
return -EBUSY;
}
@@ -2327,9 +2332,10 @@ transmit_skb(struct channel *ch, struct
}
}
+ ctc_clear_busy(ch->netdev);
return rc;
}
-\f
+
/**
* Interface API for upper network layers
*****************************************************************************/
@@ -2421,7 +2427,6 @@ ctc_tx(struct sk_buff *skb, struct net_d
dev->trans_start = jiffies;
if (transmit_skb(privptr->channel[WRITE], skb) != 0)
rc = 1;
- ctc_clear_busy(dev);
return rc;
}
@@ -2610,7 +2615,6 @@ stats_write(struct device *dev, struct d
return count;
}
-\f
static void
ctc_netdev_unregister(struct net_device * dev)
{
@@ -2685,7 +2689,6 @@ ctc_proto_store(struct device *dev, stru
return count;
}
-
static ssize_t
ctc_type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
^ permalink raw reply
* Re: [PATCH 1/2] New PowerPC 4xx on-chip ethernet controller driver
From: Jeff Garzik @ 2005-09-14 12:45 UTC (permalink / raw)
To: Eugene Surovegin; +Cc: netdev, linuxppc-embedded
In-Reply-To: <20050831050048.GB17017@gate.ebshome.net>
Eugene Surovegin wrote:
> Remove old PPC4xx EMAC driver
>
> Signed-off-by: Eugene Surovegin <ebs@ebshome.net>
Please post a diff, along with a description of the changes.
One huge patch to "remove emac driver" and another huge patch to "add
emac driver" is quite silly.
Jeff
^ permalink raw reply
* Re: [PATCH 1/3] orinoco: WE-18 support
From: Jeff Garzik @ 2005-09-14 12:31 UTC (permalink / raw)
To: Pavel Roskin
Cc: Orinoco Development List, netdev-u79uwXL29TY76Z2rM5mHXA,
Jean Tourrilhes
In-Reply-To: <1126305782.16679.5.camel@dv>
Pavel Roskin wrote:
> Author: Jean Tourrilhes <jt-sDzT885Ts8HQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Pavel Roskin <proski-mXXj517/zsQ@public.gmane.org>
>
> Use new Wireless Extension API for wireless stats.
Applied patch #1, the rest failed:
[jgarzik@pretzel netdev-2.6]$ applymbox /g/tmp/mbox ~/info/signoff.txt
Applying 'orinoco: WE-18 support'
Wrote tree 2713fbd5e71f7a7dbe94634a1bc4eb81a1ed8af5
Committed: 343c686c04eec556645f251f7d6c9b3d7335dae0
Applying 'orinoco: Update PCMCIA ID's'
error: patch failed: drivers/net/wireless/orinoco_cs.c:601
error: drivers/net/wireless/orinoco_cs.c: patch does not apply
-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
^ permalink raw reply
* Re: [PATCH 2.6.13] pcnet32: set min ring size to 4
From: Jeff Garzik @ 2005-09-14 12:28 UTC (permalink / raw)
To: Hubert WS Lin; +Cc: netdev, linux-kernel, fubar, donf, jklewis
In-Reply-To: <4325298D.1010600@tw.ibm.com>
Hubert WS Lin wrote:
> Hi,
>
> Don Fry reminded me that the pcnet32_loopback_test() asssumes the ring
> size is no less than 4. The minimum ring size was changed to 4 in
> pcnet32_set_ringparam() to allow the loopback test to work unchanged.
>
> Changelog:
> - Set minimum ring size to 4 to allow loopback test to work unchanged
> - Moved variable init_block to first field in struct pcnet32_private
>
> Signed-off-by: Hubert WS Lin <wslin@tw.ibm.com>
Patch OK, but:
Applying 'pcnet32: set min ring size to 4'
error: patch failed: drivers/net/pcnet32.c:22
error: drivers/net/pcnet32.c: patch does not apply
Please resend, without MIME attachments.
Jeff
^ permalink raw reply
* Re: Route cache performance
From: Robert Olsson @ 2005-09-14 8:04 UTC (permalink / raw)
To: Simon Kirby; +Cc: Alexey Kuznetsov, Robert Olsson, Eric Dumazet, netdev
In-Reply-To: <20050913221448.GD15704@netnation.com>
Simon Kirby writes:
> Sender: 367 Mbps, 717883 pps valid src/dst, 64 byte (Ethernet) packets
>
> 2.4.27-rc1: 297 Mbps forwarded (w/idle time?!)
> 2.4.31: 296 Mbps forwarded (w/idle time?!)
> 2.6.13-rc6: 173 Mbps forwarded
> Time permitting, I'd also like to run some profiles. It's interesting
> to note that 2.6 is slower at forwarding even straight duplicate small
> packets. We should definitely get to the bottom of that.
Yes. This is single flow? Strange.
Run a fixed size shot 10Mpkts pkts or so for both 2.4 and 2.6 and save
/proc/interrupts, proc/net/softnetstat, netstat -i, tc -s qdisc to start with.
A profile on 2.6 could solve the confusion.
Cheers.
--ro
^ permalink raw reply
* Re: [patch 9/11] net: dst_entry.refcount, use, lastuse to use alloc_percpu
From: Rusty Russell @ 2005-09-14 7:21 UTC (permalink / raw)
To: David S. Miller
Cc: kiran, akpm, linux-kernel, dipankar, bharata, shai, netdev
In-Reply-To: <20050913.162748.86496945.davem@davemloft.net>
On Tue, 2005-09-13 at 16:27 -0700, David S. Miller wrote:
> From: Ravikiran G Thirumalai <kiran@scalex86.org>
> Date: Tue, 13 Sep 2005 16:17:17 -0700
>
> > But even 1 Million dst cache entries would be 16+4 MB additional for
> > a 4 cpu box....is that too much?
>
> Absolutely.
>
> Per-cpu counters are great for things like single instance
> statistics et al. But once you start doing them per-object
> that's out of control bloat as far as I'm concerned.
This is why my original per-cpu allocator patch was damn slow, and
GFP_KERNEL only. I wasn't convinced that high-churn objects are a good
fit for spreading across cpus.
I thought that net devices and modules (which uses a primitive
hard-coded "bigref" currently) were a fair uses for bigrefs, though I'd
like to see some stats.
Cheers,
Rusty.
--
A bad analogy is like a leaky screwdriver -- Richard Braakman
^ permalink raw reply
* [PATCH 2.6.13] IPv4/IPv6: USO Scatter-gather approac
From: ananda.raju @ 2005-09-14 6:56 UTC (permalink / raw)
To: jgarzik, netdev
Cc: raghavendra.koushik, ravinandan.arakali, leonid.grossman,
rapuru.sriram, ananda.raju
Hi,
Attached below is kernel patch with UPD large send offload which address the
sendfile() syscall also. This patch uses scatter-gather support of skb to
generate large UDP datagram.
Below is a "how-to" on changes required in network drivers to use the USO
interface.
UDP Large Send Offload (USO) Interface:
--------------------------------------
USO is a feature wherein the Linux kernel network stack will offload the IP
fragmentation functionality of large UDP datagram to hardware. This will reduce
the overhead of stack in fragmenting the large UDP datagram to MTU sized packets
1) Drivers indicate their capability of USO using
dev->features |= NETIF_F_USO | NETIF_F_HW_CSUM | NETIF_F_SG
NETIF_F_HW_CSUM is required for USO over ipv6.
2) USO packet will be submitted for transmission using driver xmit routine.
USO packet will have a non-zero value for
"skb_shinfo(skb)->uso_size"
skb_shinfo(skb)->uso_size will indicate the length of data part in each IP
fragment going out of the adapter after IP fragmentation by hardware.
skb->data will contain MAC/IP/UDP header and skb_shinfo(skb)->frags[]
contains the data payload. The skb->ip_summed will be set to CHECKSUM_HW
indicating that hardware has to do checksum calculation. Hardware should
compute the UDP checksum of complete datagram and also ip header checksum of
each fragmented IP packet.
For IPV6 the USO provides the fragment identification-id in
skb_shinfo(skb)->ip6_frag_id. The adapter should use this ID for generating
IPv6 fragments.
Signed-off-by: Ananda Raju <ananda.raju@neterion.com>
---
diff -uNr linux-2.6.13/include/linux/ethtool.h linux-2.6.13_uso/include/linux/ethtool.h
--- linux-2.6.13/include/linux/ethtool.h 2005-09-07 06:36:15.000000000 -0700
+++ linux-2.6.13_uso/include/linux/ethtool.h 2005-09-07 06:32:29.000000000 -0700
@@ -261,6 +261,8 @@
int ethtool_op_set_sg(struct net_device *dev, u32 data);
u32 ethtool_op_get_tso(struct net_device *dev);
int ethtool_op_set_tso(struct net_device *dev, u32 data);
+u32 ethtool_op_get_uso(struct net_device *dev);
+int ethtool_op_set_uso(struct net_device *dev, u32 data);
/**
* ðtool_ops - Alter and report network device settings
@@ -290,6 +292,8 @@
* set_sg: Turn scatter-gather on or off
* get_tso: Report whether TCP segmentation offload is enabled
* set_tso: Turn TCP segmentation offload on or off
+ * get_uso: Report whether UDP large send offload is enabled
+ * set_uso: Turn UDP large send offload on or off
* self_test: Run specified self-tests
* get_strings: Return a set of strings that describe the requested objects
* phys_id: Identify the device
@@ -354,6 +358,8 @@
void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *);
int (*begin)(struct net_device *);
void (*complete)(struct net_device *);
+ u32 (*get_uso)(struct net_device *);
+ int (*set_uso)(struct net_device *, u32);
};
/* CMDs currently supported */
@@ -389,6 +395,8 @@
#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */
#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */
#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */
+#define ETHTOOL_GUSO 0x00000020 /* Get USO enable (ethtool_value) */
+#define ETHTOOL_SUSO 0x00000021 /* Set USO enable (ethtool_value) */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff -uNr linux-2.6.13/include/linux/netdevice.h linux-2.6.13_uso/include/linux/netdevice.h
--- linux-2.6.13/include/linux/netdevice.h 2005-09-07 04:20:51.000000000 -0700
+++ linux-2.6.13_uso/include/linux/netdevice.h 2005-09-07 04:22:51.000000000 -0700
@@ -408,6 +408,7 @@
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
#define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */
#define NETIF_F_LLTX 4096 /* LockLess TX */
+#define NETIF_F_USO 8192 /* Can offload UDP Large Send*/
/* Called after device is detached from network. */
void (*uninit)(struct net_device *dev);
diff -uNr linux-2.6.13/include/linux/skbuff.h linux-2.6.13_uso/include/linux/skbuff.h
--- linux-2.6.13/include/linux/skbuff.h 2005-09-07 04:20:56.000000000 -0700
+++ linux-2.6.13_uso/include/linux/skbuff.h 2005-09-07 04:22:58.000000000 -0700
@@ -137,6 +137,8 @@
unsigned int nr_frags;
unsigned short tso_size;
unsigned short tso_segs;
+ unsigned short uso_size;
+ unsigned int ip6_frag_id;
struct sk_buff *frag_list;
skb_frag_t frags[MAX_SKB_FRAGS];
};
@@ -327,6 +329,11 @@
extern void skb_under_panic(struct sk_buff *skb, int len,
void *here);
+extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
+ int getfrag(void *from, char *to, int offset,
+ int len,int odd, struct sk_buff *skb),
+ void *from, int length);
+
struct skb_seq_state
{
__u32 lower_offset;
diff -uNr linux-2.6.13/net/core/dev.c linux-2.6.13_uso/net/core/dev.c
--- linux-2.6.13/net/core/dev.c 2005-09-07 06:36:25.000000000 -0700
+++ linux-2.6.13_uso/net/core/dev.c 2005-09-07 06:32:02.000000000 -0700
@@ -2706,6 +2706,25 @@
dev->name);
dev->features &= ~NETIF_F_TSO;
}
+ /* TSO requires that SG is present as well. */
+ if ((dev->features & NETIF_F_TSO) &&
+ !(dev->features & NETIF_F_SG)) {
+ printk("%s: Dropping NETIF_F_TSO since no SG feature.\n",
+ dev->name);
+ dev->features &= ~NETIF_F_TSO;
+ }
+ if (dev->features & NETIF_F_USO) {
+ if (!(dev->features & NETIF_F_HW_CSUM)) {
+ printk("%s: Dropping NETIF_F_USO since no ", dev->name);
+ printk("NETIF_F_HW_CSUM feature.\n");
+ dev->features &= ~NETIF_F_USO;
+ }
+ if (!(dev->features & NETIF_F_SG)) {
+ printk("%s: Dropping NETIF_F_USO since no ", dev->name);
+ printk("NETIF_F_SG feature.\n");
+ dev->features &= ~NETIF_F_USO;
+ }
+ }
/*
* nil rebuild_header routine,
diff -uNr linux-2.6.13/net/core/ethtool.c linux-2.6.13_uso/net/core/ethtool.c
--- linux-2.6.13/net/core/ethtool.c 2005-09-07 06:36:34.000000000 -0700
+++ linux-2.6.13_uso/net/core/ethtool.c 2005-09-07 06:32:15.000000000 -0700
@@ -81,6 +81,20 @@
return 0;
}
+u32 ethtool_op_get_uso(struct net_device *dev)
+{
+ return (dev->features & NETIF_F_USO) != 0;
+}
+
+int ethtool_op_set_uso(struct net_device *dev, u32 data)
+{
+ if (data)
+ dev->features |= NETIF_F_USO;
+ else
+ dev->features &= ~NETIF_F_USO;
+ return 0;
+}
+
/* Handlers for each ethtool command */
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
@@ -469,6 +483,9 @@
err = dev->ethtool_ops->set_tso(dev, 0);
if (err)
return err;
+ err = dev->ethtool_ops->set_uso(dev, 0);
+ if (err)
+ return err;
}
return dev->ethtool_ops->set_sg(dev, data);
@@ -557,6 +574,32 @@
return dev->ethtool_ops->set_tso(dev, edata.data);
}
+static int ethtool_get_uso(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata = { ETHTOOL_GTSO };
+
+ if (!dev->ethtool_ops->get_uso)
+ return -EOPNOTSUPP;
+ edata.data = dev->ethtool_ops->get_uso(dev);
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+}
+static int ethtool_set_uso(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata;
+
+ if (!dev->ethtool_ops->set_uso)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+ if (edata.data && !(dev->features & NETIF_F_SG))
+ return -EINVAL;
+ if (edata.data && !(dev->features & NETIF_F_HW_CSUM))
+ return -EINVAL;
+ return dev->ethtool_ops->set_uso(dev, edata.data);
+}
+
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
{
struct ethtool_test test;
@@ -806,6 +849,12 @@
case ETHTOOL_GSTATS:
rc = ethtool_get_stats(dev, useraddr);
break;
+ case ETHTOOL_GUSO:
+ rc = ethtool_get_uso(dev, useraddr);
+ break;
+ case ETHTOOL_SUSO:
+ rc = ethtool_set_uso(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
@@ -833,3 +882,5 @@
EXPORT_SYMBOL(ethtool_op_set_tso);
EXPORT_SYMBOL(ethtool_op_set_tx_csum);
EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
+EXPORT_SYMBOL(ethtool_op_set_uso);
+EXPORT_SYMBOL(ethtool_op_get_uso);
diff -uNr linux-2.6.13/net/core/skbuff.c linux-2.6.13_uso/net/core/skbuff.c
--- linux-2.6.13/net/core/skbuff.c 2005-09-07 04:21:30.000000000 -0700
+++ linux-2.6.13_uso/net/core/skbuff.c 2005-09-07 06:38:57.000000000 -0700
@@ -159,6 +159,8 @@
skb_shinfo(skb)->tso_size = 0;
skb_shinfo(skb)->tso_segs = 0;
skb_shinfo(skb)->frag_list = NULL;
+ skb_shinfo(skb)->uso_size = 0;
+ skb_shinfo(skb)->ip6_frag_id = 0;
out:
return skb;
nodata:
@@ -1654,6 +1656,64 @@
return textsearch_find(config, state);
}
+/*
+ * skb_append_datato_frags - append the user data to a skb,
+ * sk - sock structure which contains skbs for transmission
+ * getfrag - The function to be called to get the data from the user.
+ * from - pointer to user message iov
+ * length - length of the iov message
+ *
+ * This procedure will allocate a skb enough to hold protocol headers and
+ * append the user data in the fragment part of the skb and add the skb to
+ * socket write queue
+ */
+int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
+ int getfrag(void *from, char *to, int offset,
+ int len,int odd, struct sk_buff *skb),
+ void *from, int length)
+{
+ int frg_cnt = 0;
+ skb_frag_t *frag = NULL;
+ struct page *page = NULL;
+ int copy, left;
+ int offset = 0;
+ do {
+ frg_cnt = skb_shinfo(skb)->nr_frags;
+ if (frg_cnt >= MAX_SKB_FRAGS) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ page = alloc_pages(sk->sk_allocation, 0);
+ if (page == NULL) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ sk->sk_sndmsg_page = page;
+ sk->sk_sndmsg_off = 0;
+ skb_fill_page_desc(skb, frg_cnt, page, 0, 0);
+ frg_cnt = skb_shinfo(skb)->nr_frags;
+ atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc);
+ skb->truesize += PAGE_SIZE;
+ frag = &skb_shinfo(skb)->frags[frg_cnt - 1];
+ left = PAGE_SIZE - frag->page_offset;
+ copy = (length > left)? left : length;
+ if (getfrag(from, page_address(frag->page) +
+ frag->page_offset+frag->size,
+ offset, copy, 0, skb) < 0) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ sk->sk_sndmsg_off += copy;
+ frag->size += copy;
+ skb->len += copy;
+ skb->data_len += copy;
+ offset += copy;
+ length -= copy;
+ page = NULL;
+ } while (length > 0);
+ return 0;
+}
+
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
@@ -1696,3 +1756,4 @@
EXPORT_SYMBOL(skb_seq_read);
EXPORT_SYMBOL(skb_abort_seq_read);
EXPORT_SYMBOL(skb_find_text);
+EXPORT_SYMBOL(skb_append_datato_frags);
diff -uNr linux-2.6.13/net/ipv4/ip_output.c linux-2.6.13_uso/net/ipv4/ip_output.c
--- linux-2.6.13/net/ipv4/ip_output.c 2005-09-07 04:21:46.000000000 -0700
+++ linux-2.6.13_uso/net/ipv4/ip_output.c 2005-09-13 07:12:05.000000000 -0700
@@ -280,7 +280,8 @@
{
IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
- if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->tso_size)
+ if (skb->len > dst_mtu(skb->dst) &&
+ !(skb_shinfo(skb)->uso_size || skb_shinfo(skb)->tso_size))
return ip_fragment(skb, ip_finish_output);
else
return ip_finish_output(skb);
@@ -781,6 +782,46 @@
csummode = CHECKSUM_HW;
inet->cork.length += length;
+ if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
+ (rt->u.dst.dev->features & NETIF_F_USO)) {
+ /* There is support for UDP large send offload by network
+ * device, so create one single skb packet containing complete
+ * udp datagram
+ */
+ if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
+ skb = sock_alloc_send_skb(sk,
+ hh_len + fragheaderlen + transhdrlen + 20,
+ (flags & MSG_DONTWAIT), &err);
+ if (skb == NULL)
+ goto error;
+ /* reserve space for Hardware header */
+ skb_reserve(skb, hh_len);
+ /* create space for UDP/IP header */
+ skb_put(skb,fragheaderlen + transhdrlen);
+ /* initialize network header pointer */
+ skb->nh.raw = skb->data;
+ /* initialize protocol header pointer */
+ skb->h.raw = skb->data + fragheaderlen;
+ skb->ip_summed = CHECKSUM_HW;
+ skb->csum = 0;
+ sk->sk_sndmsg_off = 0;
+ }
+ err = skb_append_datato_frags(sk,skb, getfrag, from,
+ (length - transhdrlen));
+ if (!err) {
+ /* specify the length of each IP datagram fragment*/
+ skb_shinfo(skb)->uso_size = (mtu - fragheaderlen);
+ __skb_queue_tail(&sk->sk_write_queue, skb);
+ return 0;
+ } else {
+ /* There is not enough support do UPD LSO,
+ * so follow normal path
+ */
+ kfree_skb(skb);
+ goto error;
+ }
+ }
+
/* So, what's going on in the loop below?
*
@@ -1012,14 +1053,23 @@
return -EINVAL;
inet->cork.length += size;
+ if ((sk->sk_protocol == IPPROTO_UDP) &&
+ (rt->u.dst.dev->features & NETIF_F_USO))
+ skb_shinfo(skb)->uso_size = (mtu - fragheaderlen);
+
while (size > 0) {
int i;
- /* Check if the remaining data fits into current packet. */
- len = mtu - skb->len;
- if (len < size)
- len = maxfraglen - skb->len;
+ if (skb_shinfo(skb)->uso_size) {
+ len = size;
+ } else {
+
+ /* Check if the remaining data fits into current packet. */
+ len = mtu - skb->len;
+ if (len < size)
+ len = maxfraglen - skb->len;
+ }
if (len <= 0) {
struct sk_buff *skb_prev;
char *data;
diff -uNr linux-2.6.13/net/ipv6/ip6_output.c linux-2.6.13_uso/net/ipv6/ip6_output.c
--- linux-2.6.13/net/ipv6/ip6_output.c 2005-09-07 04:21:57.000000000 -0700
+++ linux-2.6.13_uso/net/ipv6/ip6_output.c 2005-09-13 07:11:10.000000000 -0700
@@ -147,7 +147,8 @@
int ip6_output(struct sk_buff *skb)
{
- if (skb->len > dst_mtu(skb->dst) || dst_allfrag(skb->dst))
+ if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->uso_size) ||
+ dst_allfrag(skb->dst))
return ip6_fragment(skb, ip6_output2);
else
return ip6_output2(skb);
@@ -893,6 +894,50 @@
*/
inet->cork.length += length;
+ if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
+ (rt->u.dst.dev->features & NETIF_F_USO)) {
+ /* There is support for UDP large send offload by network
+ * device, so create one single skb packet containing complete
+ * udp datagram
+ */
+ if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
+ skb = sock_alloc_send_skb(sk,
+ hh_len + fragheaderlen + transhdrlen + 20,
+ (flags & MSG_DONTWAIT), &err);
+ if (skb == NULL)
+ goto error;
+ /* reserve space for Hardware header */
+ skb_reserve(skb, hh_len);
+ /* create space for UDP/IP header */
+ skb_put(skb,fragheaderlen + transhdrlen);
+ /* initialize network header pointer */
+ skb->nh.raw = skb->data;
+ /* initialize protocol header pointer */
+ skb->h.raw = skb->data + fragheaderlen;
+ skb->ip_summed = CHECKSUM_HW;
+ skb->csum = 0;
+ sk->sk_sndmsg_off = 0;
+ }
+ err = skb_append_datato_frags(sk,skb, getfrag, from,
+ (length - transhdrlen));
+ if (!err) {
+ struct frag_hdr fhdr;
+
+ /* specify the length of each IP datagram fragment*/
+ skb_shinfo(skb)->uso_size = (mtu - fragheaderlen) -
+ sizeof(struct frag_hdr);
+ ipv6_select_ident(skb, &fhdr);
+ skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+ __skb_queue_tail(&sk->sk_write_queue, skb);
+ return 0;
+ } else {
+ /* There is not enough support do UPD LSO,
+ * so follow normal path
+ */
+ kfree_skb(skb);
+ goto error;
+ }
+ }
if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
goto alloc_new_skb;
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox