1
0
Files
kernel-49/net/sched/cls_rsvp.h
Greg Kroah-Hartman 1eb7099fbe Merge 4.9.214 into android-4.9-q
Changes in 4.9.214
	media: iguanair: fix endpoint sanity check
	x86/cpu: Update cached HLE state on write to TSX_CTRL_CPUID_CLEAR
	sparc32: fix struct ipc64_perm type definition
	ASoC: qcom: Fix of-node refcount unbalance to link->codec_of_node
	cls_rsvp: fix rsvp_policy
	gtp: use __GFP_NOWARN to avoid memalloc warning
	net: hsr: fix possible NULL deref in hsr_handle_frame()
	net_sched: fix an OOB access in cls_tcindex
	rxrpc: Fix insufficient receive notification generation
	rxrpc: Fix NULL pointer deref due to call->conn being cleared on disconnect
	tcp: clear tp->total_retrans in tcp_disconnect()
	tcp: clear tp->delivered in tcp_disconnect()
	tcp: clear tp->data_segs{in|out} in tcp_disconnect()
	tcp: clear tp->segs_{in|out} in tcp_disconnect()
	media: uvcvideo: Avoid cyclic entity chains due to malformed USB descriptors
	mfd: dln2: More sanity checking for endpoints
	brcmfmac: Fix memory leak in brcmf_usbdev_qinit
	usb: gadget: legacy: set max_speed to super-speed
	usb: gadget: f_ncm: Use atomic_t to track in-flight request
	usb: gadget: f_ecm: Use atomic_t to track in-flight request
	ALSA: dummy: Fix PCM format loop in proc output
	media/v4l2-core: set pages dirty upon releasing DMA buffers
	media: v4l2-rect.h: fix v4l2_rect_map_inside() top/left adjustments
	lib/test_kasan.c: fix memory leak in kmalloc_oob_krealloc_more()
	powerpc/pseries: Advance pfn if section is not present in lmb_is_removable()
	mmc: spi: Toggle SPI polarity, do not hardcode it
	PCI: keystone: Fix link training retries initiation
	ubifs: Change gfp flags in page allocation for bulk read
	ubifs: Fix deadlock in concurrent bulk-read and writepage
	crypto: api - Check spawn->alg under lock in crypto_drop_spawn
	scsi: qla2xxx: Fix mtcp dump collection failure
	power: supply: ltc2941-battery-gauge: fix use-after-free
	of: Add OF_DMA_DEFAULT_COHERENT & select it on powerpc
	dm space map common: fix to ensure new block isn't already in use
	crypto: pcrypt - Do not clear MAY_SLEEP flag in original request
	crypto: atmel-aes - Fix counter overflow in CTR mode
	crypto: api - Fix race condition in crypto_spawn_alg
	crypto: picoxcell - adjust the position of tasklet_init and fix missed tasklet_kill
	btrfs: set trans->drity in btrfs_commit_transaction
	ARM: tegra: Enable PLLP bypass during Tegra124 LP1
	mwifiex: fix unbalanced locking in mwifiex_process_country_ie()
	sunrpc: expiry_time should be seconds not timeval
	KVM: x86: Refactor prefix decoding to prevent Spectre-v1/L1TF attacks
	KVM: x86: Protect DR-based index computations from Spectre-v1/L1TF attacks
	KVM: x86: Protect kvm_lapic_reg_write() from Spectre-v1/L1TF attacks
	KVM: x86: Protect kvm_hv_msr_[get|set]_crash_data() from Spectre-v1/L1TF attacks
	KVM: x86: Protect ioapic_write_indirect() from Spectre-v1/L1TF attacks
	KVM: x86: Protect MSR-based index computations in pmu.h from Spectre-v1/L1TF attacks
	KVM: x86: Protect ioapic_read_indirect() from Spectre-v1/L1TF attacks
	KVM: x86: Protect MSR-based index computations from Spectre-v1/L1TF attacks in x86.c
	KVM: x86: Protect x86_decode_insn from Spectre-v1/L1TF attacks
	KVM: x86: Protect MSR-based index computations in fixed_msr_to_seg_unit() from Spectre-v1/L1TF attacks
	KVM: PPC: Book3S HV: Uninit vCPU if vcore creation fails
	KVM: PPC: Book3S PR: Free shared page if mmu initialization fails
	KVM: x86: Free wbinvd_dirty_mask if vCPU creation fails
	clk: tegra: Mark fuse clock as critical
	scsi: qla2xxx: Fix the endianness of the qla82xx_get_fw_size() return type
	scsi: csiostor: Adjust indentation in csio_device_reset
	scsi: qla4xxx: Adjust indentation in qla4xxx_mem_free
	ext2: Adjust indentation in ext2_fill_super
	powerpc/44x: Adjust indentation in ibm4xx_denali_fixup_memsize
	NFC: pn544: Adjust indentation in pn544_hci_check_presence
	ppp: Adjust indentation into ppp_async_input
	net: smc911x: Adjust indentation in smc911x_phy_configure
	net: tulip: Adjust indentation in {dmfe, uli526x}_init_module
	IB/mlx5: Fix outstanding_pi index for GSI qps
	nfsd: fix delay timer on 32-bit architectures
	nfsd: fix jiffies/time_t mixup in LRU list
	ubi: fastmap: Fix inverted logic in seen selfcheck
	ubi: Fix an error pointer dereference in error handling code
	mfd: da9062: Fix watchdog compatible string
	mfd: rn5t618: Mark ADC control register volatile
	net: systemport: Avoid RBUF stuck in Wake-on-LAN mode
	bonding/alb: properly access headers in bond_alb_xmit()
	NFS: switch back to to ->iterate()
	NFS: Fix memory leaks and corruption in readdir
	NFS: Fix bool initialization/comparison
	NFS: Directory page cache pages need to be locked when read
	ext4: fix deadlock allocating crypto bounce page from mempool
	Btrfs: fix assertion failure on fsync with NO_HOLES enabled
	btrfs: use bool argument in free_root_pointers()
	btrfs: remove trivial locking wrappers of tree mod log
	Btrfs: fix race between adding and putting tree mod seq elements and nodes
	drm: atmel-hlcdc: enable clock before configuring timing engine
	KVM: x86: Protect pmu_intel.c from Spectre-v1/L1TF attacks
	btrfs: flush write bio if we loop in extent_write_cache_pages
	KVM: x86/mmu: Apply max PA check for MMIO sptes to 32-bit KVM
	KVM: VMX: Add non-canonical check on writes to RTIT address MSRs
	KVM: nVMX: vmread should not set rflags to specify success in case of #PF
	cifs: fail i/o on soft mounts if sessionsetup errors out
	clocksource: Prevent double add_timer_on() for watchdog_timer
	perf/core: Fix mlock accounting in perf_mmap()
	rxrpc: Fix service call disconnection
	ASoC: pcm: update FE/BE trigger order based on the command
	RDMA/netlink: Do not always generate an ACK for some netlink operations
	scsi: ufs: Fix ufshcd_probe_hba() reture value in case ufshcd_scsi_add_wlus() fails
	PCI: Don't disable bridge BARs when assigning bus resources
	nfs: NFS_SWAP should depend on SWAP
	NFSv4: try lease recovery on NFS4ERR_EXPIRED
	rtc: hym8563: Return -EINVAL if the time is known to be invalid
	rtc: cmos: Stop using shared IRQ
	ARC: [plat-axs10x]: Add missing multicast filter number to GMAC node
	ARM: dts: at91: sama5d3: fix maximum peripheral clock rates
	ARM: dts: at91: sama5d3: define clock rate range for tcb1
	tools/power/acpi: fix compilation error
	powerpc/pseries: Allow not having ibm, hypertas-functions::hcall-multi-tce for DDW
	pinctrl: sh-pfc: r8a7778: Fix duplicate SDSELF_B and SD1_CLK_B
	scsi: megaraid_sas: Do not initiate OCR if controller is not in ready state
	dm: fix potential for q->make_request_fn NULL pointer
	mwifiex: Fix possible buffer overflows in mwifiex_ret_wmm_get_status()
	mwifiex: Fix possible buffer overflows in mwifiex_cmd_append_vsie_tlv()
	libertas: don't exit from lbs_ibss_join_existing() with RCU read lock held
	libertas: make lbs_ibss_join_existing() return error code on rates overflow
	Linux 4.9.214

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: Ieac421e286126a690fd2c206ea7b4dde45e4a475
2020-03-02 17:00:48 +03:00

751 lines
18 KiB
C

/*
* net/sched/cls_rsvp.h Template file for RSVPv[46] classifiers.
*
* 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>
*/
/*
Comparing to general packet classification problem,
RSVP needs only sevaral relatively simple rules:
* (dst, protocol) are always specified,
so that we are able to hash them.
* src may be exact, or may be wildcard, so that
we can keep a hash table plus one wildcard entry.
* source port (or flow label) is important only if src is given.
IMPLEMENTATION.
We use a two level hash table: The top level is keyed by
destination address and protocol ID, every bucket contains a list
of "rsvp sessions", identified by destination address, protocol and
DPI(="Destination Port ID"): triple (key, mask, offset).
Every bucket has a smaller hash table keyed by source address
(cf. RSVP flowspec) and one wildcard entry for wildcard reservations.
Every bucket is again a list of "RSVP flows", selected by
source address and SPI(="Source Port ID" here rather than
"security parameter index"): triple (key, mask, offset).
NOTE 1. All the packets with IPv6 extension headers (but AH and ESP)
and all fragmented packets go to the best-effort traffic class.
NOTE 2. Two "port id"'s seems to be redundant, rfc2207 requires
only one "Generalized Port Identifier". So that for classic
ah, esp (and udp,tcp) both *pi should coincide or one of them
should be wildcard.
At first sight, this redundancy is just a waste of CPU
resources. But DPI and SPI add the possibility to assign different
priorities to GPIs. Look also at note 4 about tunnels below.
NOTE 3. One complication is the case of tunneled packets.
We implement it as following: if the first lookup
matches a special session with "tunnelhdr" value not zero,
flowid doesn't contain the true flow ID, but the tunnel ID (1...255).
In this case, we pull tunnelhdr bytes and restart lookup
with tunnel ID added to the list of keys. Simple and stupid 8)8)
It's enough for PIMREG and IPIP.
NOTE 4. Two GPIs make it possible to parse even GRE packets.
F.e. DPI can select ETH_P_IP (and necessary flags to make
tunnelhdr correct) in GRE protocol field and SPI matches
GRE key. Is it not nice? 8)8)
Well, as result, despite its simplicity, we get a pretty
powerful classification engine. */
struct rsvp_head {
u32 tmap[256/32];
u32 hgenerator;
u8 tgenerator;
struct rsvp_session __rcu *ht[256];
struct rcu_head rcu;
};
struct rsvp_session {
struct rsvp_session __rcu *next;
__be32 dst[RSVP_DST_LEN];
struct tc_rsvp_gpi dpi;
u8 protocol;
u8 tunnelid;
/* 16 (src,sport) hash slots, and one wildcard source slot */
struct rsvp_filter __rcu *ht[16 + 1];
struct rcu_head rcu;
};
struct rsvp_filter {
struct rsvp_filter __rcu *next;
__be32 src[RSVP_DST_LEN];
struct tc_rsvp_gpi spi;
u8 tunnelhdr;
struct tcf_result res;
struct tcf_exts exts;
u32 handle;
struct rsvp_session *sess;
struct rcu_head rcu;
};
static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
{
unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
h ^= h>>16;
h ^= h>>8;
return (h ^ protocol ^ tunnelid) & 0xFF;
}
static inline unsigned int hash_src(__be32 *src)
{
unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
h ^= h>>16;
h ^= h>>8;
h ^= h>>4;
return h & 0xF;
}
#define RSVP_APPLY_RESULT() \
{ \
int r = tcf_exts_exec(skb, &f->exts, res); \
if (r < 0) \
continue; \
else if (r > 0) \
return r; \
}
static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res)
{
struct rsvp_head *head = rcu_dereference_bh(tp->root);
struct rsvp_session *s;
struct rsvp_filter *f;
unsigned int h1, h2;
__be32 *dst, *src;
u8 protocol;
u8 tunnelid = 0;
u8 *xprt;
#if RSVP_DST_LEN == 4
struct ipv6hdr *nhptr;
if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
return -1;
nhptr = ipv6_hdr(skb);
#else
struct iphdr *nhptr;
if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
return -1;
nhptr = ip_hdr(skb);
#endif
if (unlikely(!head))
return -1;
restart:
#if RSVP_DST_LEN == 4
src = &nhptr->saddr.s6_addr32[0];
dst = &nhptr->daddr.s6_addr32[0];
protocol = nhptr->nexthdr;
xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
#else
src = &nhptr->saddr;
dst = &nhptr->daddr;
protocol = nhptr->protocol;
xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
if (ip_is_fragment(nhptr))
return -1;
#endif
h1 = hash_dst(dst, protocol, tunnelid);
h2 = hash_src(src);
for (s = rcu_dereference_bh(head->ht[h1]); s;
s = rcu_dereference_bh(s->next)) {
if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
protocol == s->protocol &&
!(s->dpi.mask &
(*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
#if RSVP_DST_LEN == 4
dst[0] == s->dst[0] &&
dst[1] == s->dst[1] &&
dst[2] == s->dst[2] &&
#endif
tunnelid == s->tunnelid) {
for (f = rcu_dereference_bh(s->ht[h2]); f;
f = rcu_dereference_bh(f->next)) {
if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
!(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
#if RSVP_DST_LEN == 4
&&
src[0] == f->src[0] &&
src[1] == f->src[1] &&
src[2] == f->src[2]
#endif
) {
*res = f->res;
RSVP_APPLY_RESULT();
matched:
if (f->tunnelhdr == 0)
return 0;
tunnelid = f->res.classid;
nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
goto restart;
}
}
/* And wildcard bucket... */
for (f = rcu_dereference_bh(s->ht[16]); f;
f = rcu_dereference_bh(f->next)) {
*res = f->res;
RSVP_APPLY_RESULT();
goto matched;
}
return -1;
}
}
return -1;
}
static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
{
struct rsvp_head *head = rtnl_dereference(tp->root);
struct rsvp_session *s;
struct rsvp_filter __rcu **ins;
struct rsvp_filter *pins;
unsigned int h1 = h & 0xFF;
unsigned int h2 = (h >> 8) & 0xFF;
for (s = rtnl_dereference(head->ht[h1]); s;
s = rtnl_dereference(s->next)) {
for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
ins = &pins->next, pins = rtnl_dereference(*ins)) {
if (pins->handle == h) {
RCU_INIT_POINTER(n->next, pins->next);
rcu_assign_pointer(*ins, n);
return;
}
}
}
/* Something went wrong if we are trying to replace a non-existant
* node. Mind as well halt instead of silently failing.
*/
BUG_ON(1);
}
static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
{
struct rsvp_head *head = rtnl_dereference(tp->root);
struct rsvp_session *s;
struct rsvp_filter *f;
unsigned int h1 = handle & 0xFF;
unsigned int h2 = (handle >> 8) & 0xFF;
if (h2 > 16)
return 0;
for (s = rtnl_dereference(head->ht[h1]); s;
s = rtnl_dereference(s->next)) {
for (f = rtnl_dereference(s->ht[h2]); f;
f = rtnl_dereference(f->next)) {
if (f->handle == handle)
return (unsigned long)f;
}
}
return 0;
}
static int rsvp_init(struct tcf_proto *tp)
{
struct rsvp_head *data;
data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
if (data) {
rcu_assign_pointer(tp->root, data);
return 0;
}
return -ENOBUFS;
}
static void rsvp_delete_filter_rcu(struct rcu_head *head)
{
struct rsvp_filter *f = container_of(head, struct rsvp_filter, rcu);
tcf_exts_destroy(&f->exts);
kfree(f);
}
static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
{
tcf_unbind_filter(tp, &f->res);
/* all classifiers are required to call tcf_exts_destroy() after rcu
* grace period, since converted-to-rcu actions are relying on that
* in cleanup() callback
*/
call_rcu(&f->rcu, rsvp_delete_filter_rcu);
}
static bool rsvp_destroy(struct tcf_proto *tp, bool force)
{
struct rsvp_head *data = rtnl_dereference(tp->root);
int h1, h2;
if (data == NULL)
return true;
if (!force) {
for (h1 = 0; h1 < 256; h1++) {
if (rcu_access_pointer(data->ht[h1]))
return false;
}
}
RCU_INIT_POINTER(tp->root, NULL);
for (h1 = 0; h1 < 256; h1++) {
struct rsvp_session *s;
while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
RCU_INIT_POINTER(data->ht[h1], s->next);
for (h2 = 0; h2 <= 16; h2++) {
struct rsvp_filter *f;
while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
rcu_assign_pointer(s->ht[h2], f->next);
rsvp_delete_filter(tp, f);
}
}
kfree_rcu(s, rcu);
}
}
kfree_rcu(data, rcu);
return true;
}
static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
{
struct rsvp_head *head = rtnl_dereference(tp->root);
struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
struct rsvp_filter __rcu **fp;
unsigned int h = f->handle;
struct rsvp_session __rcu **sp;
struct rsvp_session *nsp, *s = f->sess;
int i;
fp = &s->ht[(h >> 8) & 0xFF];
for (nfp = rtnl_dereference(*fp); nfp;
fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
if (nfp == f) {
RCU_INIT_POINTER(*fp, f->next);
rsvp_delete_filter(tp, f);
/* Strip tree */
for (i = 0; i <= 16; i++)
if (s->ht[i])
return 0;
/* OK, session has no flows */
sp = &head->ht[h & 0xFF];
for (nsp = rtnl_dereference(*sp); nsp;
sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
if (nsp == s) {
RCU_INIT_POINTER(*sp, s->next);
kfree_rcu(s, rcu);
return 0;
}
}
return 0;
}
}
return 0;
}
static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
{
struct rsvp_head *data = rtnl_dereference(tp->root);
int i = 0xFFFF;
while (i-- > 0) {
u32 h;
if ((data->hgenerator += 0x10000) == 0)
data->hgenerator = 0x10000;
h = data->hgenerator|salt;
if (rsvp_get(tp, h) == 0)
return h;
}
return 0;
}
static int tunnel_bts(struct rsvp_head *data)
{
int n = data->tgenerator >> 5;
u32 b = 1 << (data->tgenerator & 0x1F);
if (data->tmap[n] & b)
return 0;
data->tmap[n] |= b;
return 1;
}
static void tunnel_recycle(struct rsvp_head *data)
{
struct rsvp_session __rcu **sht = data->ht;
u32 tmap[256/32];
int h1, h2;
memset(tmap, 0, sizeof(tmap));
for (h1 = 0; h1 < 256; h1++) {
struct rsvp_session *s;
for (s = rtnl_dereference(sht[h1]); s;
s = rtnl_dereference(s->next)) {
for (h2 = 0; h2 <= 16; h2++) {
struct rsvp_filter *f;
for (f = rtnl_dereference(s->ht[h2]); f;
f = rtnl_dereference(f->next)) {
if (f->tunnelhdr == 0)
continue;
data->tgenerator = f->res.classid;
tunnel_bts(data);
}
}
}
}
memcpy(data->tmap, tmap, sizeof(tmap));
}
static u32 gen_tunnel(struct rsvp_head *data)
{
int i, k;
for (k = 0; k < 2; k++) {
for (i = 255; i > 0; i--) {
if (++data->tgenerator == 0)
data->tgenerator = 1;
if (tunnel_bts(data))
return data->tgenerator;
}
tunnel_recycle(data);
}
return 0;
}
static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
[TCA_RSVP_CLASSID] = { .type = NLA_U32 },
[TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) },
[TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) },
[TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
};
static int rsvp_change(struct net *net, struct sk_buff *in_skb,
struct tcf_proto *tp, unsigned long base,
u32 handle,
struct nlattr **tca,
unsigned long *arg, bool ovr)
{
struct rsvp_head *data = rtnl_dereference(tp->root);
struct rsvp_filter *f, *nfp;
struct rsvp_filter __rcu **fp;
struct rsvp_session *nsp, *s;
struct rsvp_session __rcu **sp;
struct tc_rsvp_pinfo *pinfo = NULL;
struct nlattr *opt = tca[TCA_OPTIONS];
struct nlattr *tb[TCA_RSVP_MAX + 1];
struct tcf_exts e;
unsigned int h1, h2;
__be32 *dst;
int err;
if (opt == NULL)
return handle ? -EINVAL : 0;
err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy);
if (err < 0)
return err;
err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
if (err < 0)
return err;
err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
if (err < 0)
goto errout2;
f = (struct rsvp_filter *)*arg;
if (f) {
/* Node exists: adjust only classid */
struct rsvp_filter *n;
if (f->handle != handle && handle)
goto errout2;
n = kmemdup(f, sizeof(*f), GFP_KERNEL);
if (!n) {
err = -ENOMEM;
goto errout2;
}
err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
if (err < 0) {
kfree(n);
goto errout2;
}
if (tb[TCA_RSVP_CLASSID]) {
n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
tcf_bind_filter(tp, &n->res, base);
}
tcf_exts_change(tp, &n->exts, &e);
rsvp_replace(tp, n, handle);
return 0;
}
/* Now more serious part... */
err = -EINVAL;
if (handle)
goto errout2;
if (tb[TCA_RSVP_DST] == NULL)
goto errout2;
err = -ENOBUFS;
f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
if (f == NULL)
goto errout2;
err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
if (err < 0)
goto errout;
h2 = 16;
if (tb[TCA_RSVP_SRC]) {
memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
h2 = hash_src(f->src);
}
if (tb[TCA_RSVP_PINFO]) {
pinfo = nla_data(tb[TCA_RSVP_PINFO]);
f->spi = pinfo->spi;
f->tunnelhdr = pinfo->tunnelhdr;
}
if (tb[TCA_RSVP_CLASSID])
f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
dst = nla_data(tb[TCA_RSVP_DST]);
h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
err = -ENOMEM;
if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
goto errout;
if (f->tunnelhdr) {
err = -EINVAL;
if (f->res.classid > 255)
goto errout;
err = -ENOMEM;
if (f->res.classid == 0 &&
(f->res.classid = gen_tunnel(data)) == 0)
goto errout;
}
for (sp = &data->ht[h1];
(s = rtnl_dereference(*sp)) != NULL;
sp = &s->next) {
if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
pinfo && pinfo->protocol == s->protocol &&
memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
#if RSVP_DST_LEN == 4
dst[0] == s->dst[0] &&
dst[1] == s->dst[1] &&
dst[2] == s->dst[2] &&
#endif
pinfo->tunnelid == s->tunnelid) {
insert:
/* OK, we found appropriate session */
fp = &s->ht[h2];
f->sess = s;
if (f->tunnelhdr == 0)
tcf_bind_filter(tp, &f->res, base);
tcf_exts_change(tp, &f->exts, &e);
fp = &s->ht[h2];
for (nfp = rtnl_dereference(*fp); nfp;
fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
__u32 mask = nfp->spi.mask & f->spi.mask;
if (mask != f->spi.mask)
break;
}
RCU_INIT_POINTER(f->next, nfp);
rcu_assign_pointer(*fp, f);
*arg = (unsigned long)f;
return 0;
}
}
/* No session found. Create new one. */
err = -ENOBUFS;
s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
if (s == NULL)
goto errout;
memcpy(s->dst, dst, sizeof(s->dst));
if (pinfo) {
s->dpi = pinfo->dpi;
s->protocol = pinfo->protocol;
s->tunnelid = pinfo->tunnelid;
}
sp = &data->ht[h1];
for (nsp = rtnl_dereference(*sp); nsp;
sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
break;
}
RCU_INIT_POINTER(s->next, nsp);
rcu_assign_pointer(*sp, s);
goto insert;
errout:
tcf_exts_destroy(&f->exts);
kfree(f);
errout2:
tcf_exts_destroy(&e);
return err;
}
static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
{
struct rsvp_head *head = rtnl_dereference(tp->root);
unsigned int h, h1;
if (arg->stop)
return;
for (h = 0; h < 256; h++) {
struct rsvp_session *s;
for (s = rtnl_dereference(head->ht[h]); s;
s = rtnl_dereference(s->next)) {
for (h1 = 0; h1 <= 16; h1++) {
struct rsvp_filter *f;
for (f = rtnl_dereference(s->ht[h1]); f;
f = rtnl_dereference(f->next)) {
if (arg->count < arg->skip) {
arg->count++;
continue;
}
if (arg->fn(tp, (unsigned long)f, arg) < 0) {
arg->stop = 1;
return;
}
arg->count++;
}
}
}
}
}
static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
struct sk_buff *skb, struct tcmsg *t)
{
struct rsvp_filter *f = (struct rsvp_filter *)fh;
struct rsvp_session *s;
struct nlattr *nest;
struct tc_rsvp_pinfo pinfo;
if (f == NULL)
return skb->len;
s = f->sess;
t->tcm_handle = f->handle;
nest = nla_nest_start(skb, TCA_OPTIONS);
if (nest == NULL)
goto nla_put_failure;
if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
goto nla_put_failure;
pinfo.dpi = s->dpi;
pinfo.spi = f->spi;
pinfo.protocol = s->protocol;
pinfo.tunnelid = s->tunnelid;
pinfo.tunnelhdr = f->tunnelhdr;
pinfo.pad = 0;
if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
goto nla_put_failure;
if (f->res.classid &&
nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
goto nla_put_failure;
if (((f->handle >> 8) & 0xFF) != 16 &&
nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
goto nla_put_failure;
if (tcf_exts_dump(skb, &f->exts) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
if (tcf_exts_dump_stats(skb, &f->exts) < 0)
goto nla_put_failure;
return skb->len;
nla_put_failure:
nla_nest_cancel(skb, nest);
return -1;
}
static struct tcf_proto_ops RSVP_OPS __read_mostly = {
.kind = RSVP_ID,
.classify = rsvp_classify,
.init = rsvp_init,
.destroy = rsvp_destroy,
.get = rsvp_get,
.change = rsvp_change,
.delete = rsvp_delete,
.walk = rsvp_walk,
.dump = rsvp_dump,
.owner = THIS_MODULE,
};
static int __init init_rsvp(void)
{
return register_tcf_proto_ops(&RSVP_OPS);
}
static void __exit exit_rsvp(void)
{
unregister_tcf_proto_ops(&RSVP_OPS);
}
module_init(init_rsvp)
module_exit(exit_rsvp)