diff --git a/include/linux/if.h b/include/linux/if.h index 296cd615..40eb61f9 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 00000000..228eb4eb --- /dev/null +++ b/include/linux/if_tunnel.h @@ -0,0 +1,34 @@ +#ifndef _IF_TUNNEL_H_ +#define _IF_TUNNEL_H_ + +#include + +#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 00000000..2e4dd9d3 --- /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 95bc6957..89eae5ce 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -126,5 +126,48 @@ struct xt_counters_info #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 b8362332..fc64b97a 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -148,10 +148,10 @@ struct ipt_getinfo 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 @@ struct ipt_replace 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 @@ ipt_get_target(struct ipt_entry *e) } /* 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 30b8571e..afb79d08 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -409,7 +409,8 @@ enum #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 919af93b..32761352 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -83,6 +83,8 @@ struct tc_ratespec __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 4cd0abf5..3b90a974 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -100,6 +100,13 @@ enum { 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 7028e831..0d4c15f2 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -23,7 +23,6 @@ struct __kernel_sockaddr_storage { #include /* iovec support */ #include /* 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 @@ struct ucred { #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 @@ struct ucred { #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 e21937cf..c50d2ba5 100644 --- a/include/linux/tc_ematch/tc_em_meta.h +++ b/include/linux/tc_ematch/tc_em_meta.h @@ -81,6 +81,7 @@ enum 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 51aa0425..24999dda 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -91,9 +91,9 @@ struct xfrm_replay_state }; 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 @@ enum XFRM_POLICY_IN = 0, XFRM_POLICY_OUT = 1, XFRM_POLICY_FWD = 2, + XFRM_POLICY_MASK = 3, XFRM_POLICY_MAX = 3 }; @@ -328,6 +329,7 @@ struct xfrm_usersa_info { #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 @@ struct xfrm_userpolicy_info { #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 aee526b3..3b466bfc 100644 --- a/ip/iptunnel.c +++ b/ip/iptunnel.c @@ -39,7 +39,7 @@ static void usage(void) __attribute__((noreturn)); 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 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) { int count = 0; char medium[IFNAMSIZ]; + int isatap = 0; memset(p, 0, sizeof(*p)); memset(&medium, 0, sizeof(medium)); @@ -90,6 +91,13 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) 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 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) 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 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) 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 b727422c..bec9db03 100644 --- a/tc/em_meta.c +++ b/tc/em_meta.c @@ -109,6 +109,7 @@ struct meta_entry { __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 f2a9305d..042fe8b8 100644 --- a/tc/m_ipt.c +++ b/tc/m_ipt.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "utils.h" #include "tc_util.h" diff --git a/tc/q_netem.c b/tc/q_netem.c index f08b9c19..d06932e8 100644 --- a/tc/q_netem.c +++ b/tc/q_netem.c @@ -44,14 +44,18 @@ static void explain1(const char *arg) #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 @@ static int get_distribution(const char *type, __s16 *data) if (endp == p) break; - if (n >= MAXDIST) { + if (n >= maxdata) { fprintf(stderr, "%s: too much data\n", name); n = -1; @@ -236,10 +240,12 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, } } 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 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, 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 00000000..c90796d4 --- /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, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, +}; +