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