Merge branch 'net-2.6.25'
diff --git a/include/linux/if.h b/include/linux/if.h
index 296cd61..40eb61f 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -49,7 +49,9 @@
 #define IFF_LOWER_UP	0x10000		/* driver signals L1 up		*/
 #define IFF_DORMANT	0x20000		/* driver signals dormant	*/
 
-#define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|\
+#define IFF_ECHO	0x40000		/* echo sent packets		*/
+
+#define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
 		IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
 
 /* Private (from user) interface flags (netdevice->priv_flags). */
@@ -60,6 +62,7 @@
 #define IFF_MASTER_ALB	0x10		/* bonding master, balance-alb.	*/
 #define IFF_BONDING	0x20		/* bonding master or slave	*/
 #define IFF_SLAVE_NEEDARP 0x40		/* need ARPs for validation	*/
+#define IFF_ISATAP	0x80		/* ISATAP interface (RFC4214)	*/
 
 #define IF_GET_IFACE	0x0001		/* for querying only */
 #define IF_GET_PROTO	0x0002
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
new file mode 100644
index 0000000..228eb4e
--- /dev/null
+++ b/include/linux/if_tunnel.h
@@ -0,0 +1,34 @@
+#ifndef _IF_TUNNEL_H_
+#define _IF_TUNNEL_H_
+
+#include <linux/types.h>
+
+#define SIOCGETTUNNEL   (SIOCDEVPRIVATE + 0)
+#define SIOCADDTUNNEL   (SIOCDEVPRIVATE + 1)
+#define SIOCDELTUNNEL   (SIOCDEVPRIVATE + 2)
+#define SIOCCHGTUNNEL   (SIOCDEVPRIVATE + 3)
+
+#define GRE_CSUM	__constant_htons(0x8000)
+#define GRE_ROUTING	__constant_htons(0x4000)
+#define GRE_KEY		__constant_htons(0x2000)
+#define GRE_SEQ		__constant_htons(0x1000)
+#define GRE_STRICT	__constant_htons(0x0800)
+#define GRE_REC		__constant_htons(0x0700)
+#define GRE_FLAGS	__constant_htons(0x00F8)
+#define GRE_VERSION	__constant_htons(0x0007)
+
+/* i_flags values for SIT mode */
+#define	SIT_ISATAP	0x0001
+
+struct ip_tunnel_parm
+{
+	char			name[IFNAMSIZ];
+	int			link;
+	__be16			i_flags;
+	__be16			o_flags;
+	__be32			i_key;
+	__be32			o_key;
+	struct iphdr		iph;
+};
+
+#endif /* _IF_TUNNEL_H_ */
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
new file mode 100644
index 0000000..2e4dd9d
--- /dev/null
+++ b/include/linux/netfilter.h
@@ -0,0 +1,47 @@
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number. Not nice, but better than additional function arguments. */
+#define NF_VERDICT_MASK 0x0000ffff
+#define NF_VERDICT_BITS 16
+
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) (((x << NF_VERDICT_QBITS) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+/* only for userspace compatibility */
+/* Generic cache responses from hook functions.
+   <= 0x2000 is used for protocol-flags. */
+#define NFC_UNKNOWN 0x4000
+#define NFC_ALTERED 0x8000
+
+enum nf_inet_hooks {
+	NF_INET_PRE_ROUTING,
+	NF_INET_LOCAL_IN,
+	NF_INET_FORWARD,
+	NF_INET_LOCAL_OUT,
+	NF_INET_POST_ROUTING,
+	NF_INET_NUMHOOKS
+};
+
+union nf_inet_addr {
+	u_int32_t	all[4];
+	__be32		ip;
+	__be32		ip6[4];
+	struct in_addr	in;
+	struct in6_addr	in6;
+};
+
+#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index 95bc695..89eae5c 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -126,5 +126,48 @@
 
 #define XT_INV_PROTO		0x40	/* Invert the sense of PROTO. */
 
+/* fn returns 0 to continue iteration */
+#define XT_MATCH_ITERATE(type, e, fn, args...)			\
+({								\
+	unsigned int __i;					\
+	int __ret = 0;						\
+	struct xt_entry_match *__m;				\
+								\
+	for (__i = sizeof(type);				\
+	     __i < (e)->target_offset;				\
+	     __i += __m->u.match_size) {			\
+		__m = (void *)e + __i;				\
+								\
+		__ret = fn(__m , ## args);			\
+		if (__ret != 0)					\
+			break;					\
+	}							\
+	__ret;							\
+})
+
+/* fn returns 0 to continue iteration */
+#define XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
+({								\
+	unsigned int __i, __n;					\
+	int __ret = 0;						\
+	type *__entry;						\
+								\
+	for (__i = 0, __n = 0; __i < (size);			\
+	     __i += __entry->next_offset, __n++) { 		\
+		__entry = (void *)(entries) + __i;		\
+		if (__n < n)					\
+			continue;				\
+								\
+		__ret = fn(__entry , ## args);			\
+		if (__ret != 0)					\
+			break;					\
+	}							\
+	__ret;							\
+})
+
+/* fn returns 0 to continue iteration */
+#define XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
+	XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
+
 
 #endif /* _X_TABLES_H */
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
index b836233..fc64b97 100644
--- a/include/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -148,10 +148,10 @@
 	unsigned int valid_hooks;
 
 	/* Hook entry points: one per netfilter hook. */
-	unsigned int hook_entry[NF_IP_NUMHOOKS];
+	unsigned int hook_entry[NF_INET_NUMHOOKS];
 
 	/* Underflow points. */
-	unsigned int underflow[NF_IP_NUMHOOKS];
+	unsigned int underflow[NF_INET_NUMHOOKS];
 
 	/* Number of entries */
 	unsigned int num_entries;
@@ -177,10 +177,10 @@
 	unsigned int size;
 
 	/* Hook entry points. */
-	unsigned int hook_entry[NF_IP_NUMHOOKS];
+	unsigned int hook_entry[NF_INET_NUMHOOKS];
 
 	/* Underflow points. */
-	unsigned int underflow[NF_IP_NUMHOOKS];
+	unsigned int underflow[NF_INET_NUMHOOKS];
 
 	/* Information about old entries: */
 	/* Number of counters (must be equal to current number of entries). */
@@ -221,60 +221,12 @@
 }
 
 /* fn returns 0 to continue iteration */
-#define IPT_MATCH_ITERATE(e, fn, args...)	\
-({						\
-	unsigned int __i;			\
-	int __ret = 0;				\
-	struct ipt_entry_match *__match;	\
-						\
-	for (__i = sizeof(struct ipt_entry);	\
-	     __i < (e)->target_offset;		\
-	     __i += __match->u.match_size) {	\
-		__match = (void *)(e) + __i;	\
-						\
-		__ret = fn(__match , ## args);	\
-		if (__ret != 0)			\
-			break;			\
-	}					\
-	__ret;					\
-})
+#define IPT_MATCH_ITERATE(e, fn, args...) \
+	XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
 
 /* fn returns 0 to continue iteration */
-#define IPT_ENTRY_ITERATE(entries, size, fn, args...)		\
-({								\
-	unsigned int __i;					\
-	int __ret = 0;						\
-	struct ipt_entry *__entry;				\
-								\
-	for (__i = 0; __i < (size); __i += __entry->next_offset) { \
-		__entry = (void *)(entries) + __i;		\
-								\
-		__ret = fn(__entry , ## args);			\
-		if (__ret != 0)					\
-			break;					\
-	}							\
-	__ret;							\
-})
-
-/* fn returns 0 to continue iteration */
-#define IPT_ENTRY_ITERATE_CONTINUE(entries, size, n, fn, args...) \
-({								\
-	unsigned int __i, __n;					\
-	int __ret = 0;						\
-	struct ipt_entry *__entry;				\
-								\
-	for (__i = 0, __n = 0; __i < (size);			\
-	     __i += __entry->next_offset, __n++) { 		\
-		__entry = (void *)(entries) + __i;		\
-		if (__n < n)					\
-			continue;				\
-								\
-		__ret = fn(__entry , ## args);			\
-		if (__ret != 0)					\
-			break;					\
-	}							\
-	__ret;							\
-})
+#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
+	XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
 
 /*
  *	Main firewall chains definitions and global var's definitions.
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 30b8571..afb79d0 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -409,7 +409,8 @@
 #define	TCF_EM_U32		3
 #define	TCF_EM_META		4
 #define	TCF_EM_TEXT		5
-#define	TCF_EM_MAX		5
+#define TCF_EM_VLAN		6
+#define	TCF_EM_MAX		6
 
 enum
 {
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 919af93..3276135 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -83,6 +83,8 @@
 	__u32		rate;
 };
 
+#define TC_RTAB_SIZE	1024
+
 /* FIFO section */
 
 struct tc_fifo_qopt
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 4cd0abf..3b90a97 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -100,6 +100,13 @@
 	RTM_NEWNDUSEROPT = 68,
 #define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
 
+	RTM_NEWADDRLABEL = 72,
+#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
+	RTM_DELADDRLABEL,
+#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
+	RTM_GETADDRLABEL,
+#define RTM_GETADDRLABEL RTM_GETADDRLABEL
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 7028e83..0d4c15f 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -23,7 +23,6 @@
 #include <linux/uio.h>			/* iovec support		*/
 #include <linux/types.h>		/* pid_t			*/
 
-extern int sysctl_somaxconn;
 #ifdef CONFIG_PROC_FS
 struct seq_file;
 extern void socket_seq_show(struct seq_file *seq);
@@ -182,6 +181,7 @@
 #define AF_PPPOX	24	/* PPPoX sockets		*/
 #define AF_WANPIPE	25	/* Wanpipe API Sockets */
 #define AF_LLC		26	/* Linux LLC			*/
+#define AF_CAN		29	/* Controller Area Network      */
 #define AF_TIPC		30	/* TIPC sockets			*/
 #define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
 #define AF_IUCV		32	/* IUCV sockets			*/
@@ -217,6 +217,7 @@
 #define PF_PPPOX	AF_PPPOX
 #define PF_WANPIPE	AF_WANPIPE
 #define PF_LLC		AF_LLC
+#define PF_CAN		AF_CAN
 #define PF_TIPC		AF_TIPC
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #define PF_IUCV		AF_IUCV
diff --git a/include/linux/tc_ematch/tc_em_meta.h b/include/linux/tc_ematch/tc_em_meta.h
index e21937c..c50d2ba 100644
--- a/include/linux/tc_ematch/tc_em_meta.h
+++ b/include/linux/tc_ematch/tc_em_meta.h
@@ -81,6 +81,7 @@
  	TCF_META_ID_SK_SNDTIMEO,
  	TCF_META_ID_SK_SENDMSG_OFF,
  	TCF_META_ID_SK_WRITE_PENDING,
+	TCF_META_ID_VLAN_TAG,
 	__TCF_META_ID_MAX
 };
 #define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1)
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 51aa042..24999dd 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -91,9 +91,9 @@
 };
 
 struct xfrm_algo {
-	char	alg_name[64];
-	int	alg_key_len;    /* in bits */
-	char	alg_key[0];
+	char		alg_name[64];
+	unsigned int	alg_key_len;    /* in bits */
+	char		alg_key[0];
 };
 
 struct xfrm_stats {
@@ -114,6 +114,7 @@
 	XFRM_POLICY_IN	= 0,
 	XFRM_POLICY_OUT	= 1,
 	XFRM_POLICY_FWD	= 2,
+	XFRM_POLICY_MASK = 3,
 	XFRM_POLICY_MAX	= 3
 };
 
@@ -328,6 +329,7 @@
 #define XFRM_STATE_DECAP_DSCP	2
 #define XFRM_STATE_NOPMTUDISC	4
 #define XFRM_STATE_WILDRECV	8
+#define XFRM_STATE_ICMP		16
 };
 
 struct xfrm_usersa_id {
@@ -362,6 +364,8 @@
 #define XFRM_POLICY_BLOCK	1
 	__u8				flags;
 #define XFRM_POLICY_LOCALOK	1	/* Allow user to override global policy */
+	/* Automatically expand selector to include matching ICMP payloads. */
+#define XFRM_POLICY_ICMP	2
 	__u8				share;
 };
 
diff --git a/ip/iptunnel.c b/ip/iptunnel.c
index aee526b..3b466bf 100644
--- a/ip/iptunnel.c
+++ b/ip/iptunnel.c
@@ -39,7 +39,7 @@
 static void usage(void)
 {
 	fprintf(stderr, "Usage: ip tunnel { add | change | del | show } [ NAME ]\n");
-	fprintf(stderr, "          [ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ mode { ipip | gre | sit | isatap } ] [ remote ADDR ] [ local ADDR ]\n");
 	fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
 	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
 	fprintf(stderr, "\n");
@@ -55,6 +55,7 @@
 {
 	int count = 0;
 	char medium[IFNAMSIZ];
+	int isatap = 0;
 
 	memset(p, 0, sizeof(*p));
 	memset(&medium, 0, sizeof(medium));
@@ -90,6 +91,13 @@
 					exit(-1);
 				}
 				p->iph.protocol = IPPROTO_IPV6;
+			} else if (strcmp(*argv, "isatap") == 0) {
+				if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+					fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
+					exit(-1);
+				}
+				p->iph.protocol = IPPROTO_IPV6;
+				isatap++;
 			} else {
 				fprintf(stderr,"Cannot guess tunnel mode.\n");
 				exit(-1);
@@ -212,6 +220,10 @@
 			p->iph.protocol = IPPROTO_IPIP;
 		else if (memcmp(p->name, "sit", 3) == 0)
 			p->iph.protocol = IPPROTO_IPV6;
+		else if (memcmp(p->name, "isatap", 6) == 0) {
+			p->iph.protocol = IPPROTO_IPV6;
+			isatap++;
+		}
 	}
 
 	if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
@@ -239,6 +251,14 @@
 		fprintf(stderr, "Broadcast tunnel requires a source address.\n");
 		return -1;
 	}
+	if (isatap) {
+		if (p->iph.daddr) {
+			fprintf(stderr, "no remote with isatap.\n");
+			return -1;
+		}
+		p->i_flags |= SIT_ISATAP;
+	}
+
 	return 0;
 }
 
diff --git a/tc/em_meta.c b/tc/em_meta.c
index b727422..bec9db0 100644
--- a/tc/em_meta.c
+++ b/tc/em_meta.c
@@ -109,6 +109,7 @@
 	__A(SK_ERR_QLEN,	"sk_err_queue",	"i",	"Error queue length"),
 	__A(SK_FORWARD_ALLOCS,	"sk_fwd_alloc",	"i",	"Forward allocations"),
 	__A(SK_SNDBUF,		"sk_sndbuf",	"i",	"Send buffer size"),
+	__A(VLAN_TAG,		"vlan",		"i",	"Vlan tag"),
 #undef __A
 };
 
diff --git a/tc/m_ipt.c b/tc/m_ipt.c
index f2a9305..042fe8b 100644
--- a/tc/m_ipt.c
+++ b/tc/m_ipt.c
@@ -16,6 +16,7 @@
 #include <arpa/inet.h>
 #include <linux/if.h>
 #include <iptables.h>
+#include <linux/netfilter.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include "utils.h"
 #include "tc_util.h"
diff --git a/tc/q_netem.c b/tc/q_netem.c
index f08b9c1..d06932e 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -44,14 +44,18 @@
 
 #define usage() return(-1)
 
+/* Upper bound on size of distribution 
+ *  really (TCA_BUF_MAX - other headers) / sizeof (__s16)
+ */
+#define MAX_DIST	(16*1024)
+
 /*
  * Simplistic file parser for distrbution data.
  * Format is:
  *	# comment line(s)
- *	data0 data1
+ *	data0 data1 ...
  */
-#define MAXDIST	65536
-static int get_distribution(const char *type, __s16 *data)
+static int get_distribution(const char *type, __s16 *data, int maxdata)
 {
 	FILE *f;
 	int n;
@@ -78,7 +82,7 @@
 			if (endp == p)
 				break;
 
-			if (n >= MAXDIST) {
+			if (n >= maxdata) {
 				fprintf(stderr, "%s: too much data\n",
 					name);
 				n = -1;
@@ -236,10 +240,12 @@
 			}
 		} else if (matches(*argv, "distribution") == 0) {
 			NEXT_ARG();
-			dist_data = alloca(MAXDIST);
-			dist_size = get_distribution(*argv, dist_data);
-			if (dist_size < 0)
+			dist_data = calloc(sizeof(dist_data[0]), MAX_DIST);
+			dist_size = get_distribution(*argv, dist_data, MAX_DIST);
+			if (dist_size <= 0) {
+				free(dist_data);
 				return -1;
+			}
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -271,25 +277,27 @@
 		return -1;
 	}
 
-	if (addattr_l(n, TCA_BUF_MAX, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
+	if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
 		return -1;
 
 	if (present[TCA_NETEM_CORR] &&
-	    addattr_l(n, TCA_BUF_MAX, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
+	    addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
 			return -1;
 
 	if (present[TCA_NETEM_REORDER] && 
-	    addattr_l(n, TCA_BUF_MAX, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
+	    addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
 		return -1;
 
 	if (present[TCA_NETEM_CORRUPT] &&
-	    addattr_l(n, TCA_BUF_MAX, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
+	    addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
 		return -1;
 
 	if (dist_data) {
-		if (addattr_l(n, 32768, TCA_NETEM_DELAY_DIST,
-			      dist_data, dist_size*sizeof(dist_data[0])) < 0)
+		if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
+			      TCA_NETEM_DELAY_DIST,
+			      dist_data, dist_size * sizeof(dist_data[0])) < 0)
 			return -1;
+		free(dist_data);
 	}
 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
 	return 0;
diff --git a/tc/q_rlim.c b/tc/q_rlim.c
new file mode 100644
index 0000000..c90796d
--- /dev/null
+++ b/tc/q_rlim.c
@@ -0,0 +1,129 @@
+/*
+ * q_rtlim.c		RTLIM.
+ *
+ *		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 the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, 
+		"Usage: ... rlim limit PACKETS rate KBPS [ overhead BYTES ]\n");
+}
+
+static void explain1(char *arg)
+{
+	fprintf(stderr, "Illegal \"%s\"\n", arg);
+}
+
+
+#define usage() return(-1)
+
+static int rlim_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+	unsigned x;
+	struct tc_rlim_qopt opt = { 
+		.overhead = 24,		/* Ether IPG + Preamble + CRC */
+	};
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (matches(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (opt.limit) {
+				fprintf(stderr, "Double \"limit\" spec\n");
+				return -1;
+			}
+			if (get_size(&opt.limit, *argv)) {
+				explain1("limit");
+				return -1;
+			}
+		} else if (strcmp(*argv, "rate") == 0) {
+			NEXT_ARG();
+			if (opt.rate) {
+				fprintf(stderr, "Double \"rate\" spec\n");
+				return -1;
+			}
+
+			if (get_rate(&x, *argv)) {
+				explain1("rate");
+				return -1;
+			}
+			opt.rate = x;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	if (opt.rate == 0) {
+		fprintf(stderr, "\"rate\" is required.\n");
+		return -1;
+	}
+
+	if (opt.limit == 0)
+		opt.limit = 1000;
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	addattr_l(n, 2024, TCA_RLIM_PARMS, &opt, sizeof(opt));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+	return 0;
+}
+
+static int rlim_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_RLIM_PARMS+1];
+	struct tc_rlim_qopt *qopt;
+	SPRINT_BUF(b1);
+	SPRINT_BUF(b2);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_RLIM_PARMS, opt);
+	if (tb[TCA_RLIM_PARMS] == NULL)
+		return -1;
+
+	qopt = RTA_DATA(tb[TCA_RLIM_PARMS]);
+	if (RTA_PAYLOAD(tb[TCA_RLIM_PARMS])  < sizeof(*qopt))
+		return -1;
+
+	fprintf(f, "limit %s rate %s overhead %u", 
+		sprint_size(qopt->limit, b1),
+		sprint_rate(qopt->rate, b2),
+		qopt->overhead);
+
+	return 0;
+}
+
+struct qdisc_util rlim_qdisc_util = {
+	.id		= "rlim",
+	.parse_qopt	= rlim_parse_opt,
+	.print_qopt	= rlim_print_opt,
+};
+