Commit 9a3bc4fd authored by David S. Miller's avatar David S. Miller

Merge davem@nuts.ninka.net:/home/davem/src/BK/net-2.5

into kernel.bkbits.net:/home/davem/net-2.5
parents 004662fb cff5b2a4
......@@ -198,4 +198,10 @@ struct ip_esp_hdr {
__u8 enc_data[0]; /* Variable len but >=8. Mind the 64 bit alignment! */
};
struct ip_comp_hdr {
__u8 nexthdr;
__u8 flags;
__u16 cpi;
};
#endif /* _LINUX_IP_H */
......@@ -89,6 +89,12 @@ struct ipv6_esp_hdr {
__u8 enc_data[0]; /* Length variable but >=8. Mind the 64 bit alignment! */
};
struct ipv6_comp_hdr {
__u8 nexthdr;
__u8 flags;
__u16 cpi;
};
/*
* IPv6 fixed header
*
......
#ifndef _NET_IPCOMP_H
#define _NET_IPCOMP_H
#define IPCOMP_SCRATCH_SIZE 65400
struct ipcomp_data {
u16 threshold;
u8 *scratch;
struct crypto_tfm *tfm;
};
#endif
......@@ -315,6 +315,7 @@ extern int ip6_build_xmit(struct sock *sk,
unsigned length,
struct ipv6_txoptions *opt,
int hlimit, int flags);
extern int ip6_found_nexthdr(struct sk_buff *skb, u8 **nexthdr);
extern int ip6_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb),
......
......@@ -125,4 +125,36 @@ extern void ipx_proc_exit(void);
extern const char *ipx_frame_name(unsigned short);
extern const char *ipx_device_name(struct ipx_interface *intrfc);
static __inline__ void ipxitf_hold(struct ipx_interface *intrfc)
{
atomic_inc(&intrfc->refcnt);
}
extern void ipxitf_down(struct ipx_interface *intrfc);
static __inline__ void ipxitf_put(struct ipx_interface *intrfc)
{
if (atomic_dec_and_test(&intrfc->refcnt))
ipxitf_down(intrfc);
}
extern void __ipxitf_down(struct ipx_interface *intrfc);
static __inline__ void __ipxitf_put(struct ipx_interface *intrfc)
{
if (atomic_dec_and_test(&intrfc->refcnt))
__ipxitf_down(intrfc);
}
static __inline__ void ipxrtr_hold(struct ipx_route *rt)
{
atomic_inc(&rt->refcnt);
}
static __inline__ void ipxrtr_put(struct ipx_route *rt)
{
if (atomic_dec_and_test(&rt->refcnt))
kfree(rt);
}
#endif /* _NET_INET_IPX_H_ */
......@@ -83,7 +83,7 @@ int try_atm_clip_ops(void)
#ifdef CONFIG_ATM_CLIP_MODULE
EXPORT_SYMBOL(atm_clip_ops);
EXPORT_SYMBOL(atm_clip_ops_mutex);
EXPORT_SYMBOL(try_atm_clip_ops);
EXPORT_SYMBOL(atm_clip_ops_set);
#endif
#endif
......
......@@ -674,7 +674,7 @@ int __init atm_proc_init(void)
return -ENOMEM;
}
void __exit atm_proc_exit(void)
void atm_proc_exit(void)
{
atm_proc_cleanup();
}
/*
* IP Payload Compression Protocol (IPComp) - RFC3713.
* IP Payload Compression Protocol (IPComp) - RFC3173.
*
* Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
*
......@@ -22,20 +22,7 @@
#include <net/xfrm.h>
#include <net/icmp.h>
#include <net/esp.h>
#define IPCOMP_SCRATCH_SIZE 65400
struct ipcomp_hdr {
u8 nexthdr;
u8 flags;
u16 cpi;
};
struct ipcomp_data {
u16 threshold;
u8 *scratch;
struct crypto_tfm *tfm;
};
#include <net/ipcomp.h>
static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
{
......@@ -52,7 +39,7 @@ static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
if (err)
goto out;
if (dlen < (plen + sizeof(struct ipcomp_hdr))) {
if (dlen < (plen + sizeof(struct ip_comp_hdr))) {
err = -EINVAL;
goto out;
}
......@@ -93,11 +80,11 @@ static int ipcomp_input(struct xfrm_state *x,
iph = skb->nh.iph;
memcpy(&tmp_iph, iph, iph->ihl * 4);
nexthdr = *(u8 *)skb->data;
skb_pull(skb, sizeof(struct ipcomp_hdr));
skb->nh.raw += sizeof(struct ipcomp_hdr);
skb_pull(skb, sizeof(struct ip_comp_hdr));
skb->nh.raw += sizeof(struct ip_comp_hdr);
memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4);
iph = skb->nh.iph;
iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ipcomp_hdr));
iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr));
iph->protocol = nexthdr;
skb->h.raw = skb->data;
err = ipcomp_decompress(x, skb);
......@@ -122,7 +109,7 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
if (err)
goto out;
if ((dlen + sizeof(struct ipcomp_hdr)) >= plen) {
if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) {
err = -EMSGSIZE;
goto out;
}
......@@ -162,7 +149,7 @@ static int ipcomp_output(struct sk_buff *skb)
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct iphdr *iph, *top_iph;
struct ipcomp_hdr *ipch;
struct ip_comp_hdr *ipch;
struct ipcomp_data *ipcd = x->data;
union {
struct iphdr iph;
......@@ -215,13 +202,13 @@ static int ipcomp_output(struct sk_buff *skb)
/* Install ipcomp header, convert into ipcomp datagram. */
iph = skb->nh.iph;
memcpy(&tmp_iph, iph, iph->ihl * 4);
top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ipcomp_hdr));
top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ip_comp_hdr));
memcpy(top_iph, &tmp_iph, iph->ihl * 4);
iph = top_iph;
iph->tot_len = htons(skb->len);
iph->protocol = IPPROTO_COMP;
iph->check = 0;
ipch = (struct ipcomp_hdr *)((char *)iph + iph->ihl * 4);
ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
ipch->nexthdr = x->props.mode ? IPPROTO_IPIP : tmp_iph.iph.protocol;
ipch->flags = 0;
ipch->cpi = htons((u16 )ntohl(x->id.spi));
......@@ -252,7 +239,7 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
{
u32 spi;
struct iphdr *iph = (struct iphdr *)skb->data;
struct ipcomp_hdr *ipch = (struct ipcomp_hdr *)(skb->data+(iph->ihl<<2));
struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
struct xfrm_state *x;
if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
......@@ -356,7 +343,7 @@ static int ipcomp_init_state(struct xfrm_state *x, void *args)
goto error;
memset(ipcd, 0, sizeof(*ipcd));
x->props.header_len = sizeof(struct ipcomp_hdr);
x->props.header_len = sizeof(struct ip_comp_hdr);
if (x->props.mode)
x->props.header_len += sizeof(struct iphdr);
x->data = ipcd;
......@@ -433,6 +420,6 @@ module_init(ipcomp4_init);
module_exit(ipcomp4_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3713");
MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173");
MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
......@@ -33,4 +33,13 @@ config INET6_ESP
If unsure, say Y.
config INET6_IPCOMP
tristate "IPv6: IPComp transformation"
depends on IPV6
---help---
Support for IP Paylod Compression (RFC3173), typically needed
for IPsec.
If unsure, say Y.
source "net/ipv6/netfilter/Kconfig"
......@@ -13,4 +13,5 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
obj-$(CONFIG_INET6_AH) += ah6.o
obj-$(CONFIG_INET6_ESP) += esp6.o
obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
obj-$(CONFIG_NETFILTER) += netfilter/
......@@ -887,7 +887,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
#endif
}
static int ip6_found_nexthdr(struct sk_buff *skb, u8 **nexthdr)
int ip6_found_nexthdr(struct sk_buff *skb, u8 **nexthdr)
{
u16 offset = sizeof(struct ipv6hdr);
struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
......
/*
* IP Payload Compression Protocol (IPComp) for IPv6 - RFC3173
*
* Copyright (C)2003 USAGI/WIDE Project
*
* Author Mitsuru KANDA <mk@linux-ipv6.org>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* [Memo]
*
* Outbound:
* The compression of IP datagram MUST be done before AH/ESP processing,
* fragmentation, and the addition of Hop-by-Hop/Routing header.
*
* Inbound:
* The decompression of IP datagram MUST be done after the reassembly,
* AH/ESP processing.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <net/ip.h>
#include <net/xfrm.h>
#include <net/ipcomp.h>
#include <asm/scatterlist.h>
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
#include <linux/random.h>
#include <net/icmp.h>
#include <net/ipv6.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
/* XXX no ipv6 ipcomp specific */
#define NIP6(addr) \
ntohs((addr).s6_addr16[0]),\
ntohs((addr).s6_addr16[1]),\
ntohs((addr).s6_addr16[2]),\
ntohs((addr).s6_addr16[3]),\
ntohs((addr).s6_addr16[4]),\
ntohs((addr).s6_addr16[5]),\
ntohs((addr).s6_addr16[6]),\
ntohs((addr).s6_addr16[7])
static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
int err = 0;
u8 nexthdr = 0;
u8 *prevhdr;
int hdr_len = skb->h.raw - skb->nh.raw;
unsigned char *tmp_hdr = NULL;
struct ipv6hdr *iph;
int plen, dlen;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch = ipcd->scratch;
if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
err = -ENOMEM;
goto out;
}
skb->ip_summed = CHECKSUM_NONE;
/* Remove ipcomp header and decompress original payload */
iph = skb->nh.ipv6h;
tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr)
goto out;
memcpy(tmp_hdr, iph, hdr_len);
nexthdr = *(u8 *)skb->data;
skb_pull(skb, sizeof(struct ipv6_comp_hdr));
skb->nh.raw += sizeof(struct ipv6_comp_hdr);
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
iph = skb->nh.ipv6h;
iph->payload_len = htons(ntohs(iph->payload_len) - sizeof(struct ipv6_comp_hdr));
skb->h.raw = skb->data;
/* decompression */
plen = skb->len;
dlen = IPCOMP_SCRATCH_SIZE;
start = skb->data;
err = crypto_comp_decompress(ipcd->tfm, start, plen, scratch, &dlen);
if (err) {
err = -EINVAL;
goto out;
}
if (dlen < (plen + sizeof(struct ipv6_comp_hdr))) {
err = -EINVAL;
goto out;
}
err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
if (err) {
goto out;
}
skb_put(skb, dlen - plen);
memcpy(skb->data, scratch, dlen);
iph = skb->nh.ipv6h;
iph->payload_len = htons(skb->len);
ip6_found_nexthdr(skb, &prevhdr);
*prevhdr = nexthdr;
out:
if (tmp_hdr)
kfree(tmp_hdr);
if (err)
goto error_out;
return nexthdr;
error_out:
return err;
}
static int ipcomp6_output(struct sk_buff *skb)
{
int err;
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *tmp_iph = NULL, *iph, *top_iph;
int hdr_len = 0;
struct ipv6_comp_hdr *ipch;
struct ipcomp_data *ipcd = x->data;
u8 *prevhdr;
u8 nexthdr = 0;
int plen, dlen;
u8 *start, *scratch = ipcd->scratch;
if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
err = -EINVAL;
goto error_nolock;
}
spin_lock_bh(&x->lock);
err = xfrm_check_output(x, skb, AF_INET6);
if (err)
goto error;
if (x->props.mode) {
hdr_len = sizeof(struct ipv6hdr);
nexthdr = IPPROTO_IPV6;
iph = skb->nh.ipv6h;
top_iph = (struct ipv6hdr *)skb_push(skb, sizeof(struct ipv6hdr));
top_iph->version = 6;
top_iph->priority = iph->priority;
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
top_iph->nexthdr = IPPROTO_IPV6; /* initial */
top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
top_iph->hop_limit = iph->hop_limit;
memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr));
memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr));
skb->nh.raw = skb->data; /* == top_iph */
skb->h.raw = skb->nh.raw + hdr_len;
} else {
hdr_len = ip6_found_nexthdr(skb, &prevhdr);
nexthdr = *prevhdr;
}
/* check whether datagram len is larger than threshold */
if ((skb->len - hdr_len) < ipcd->threshold) {
goto out_ok;
}
if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
err = -ENOMEM;
goto error;
}
/* compression */
plen = skb->len - hdr_len;
dlen = IPCOMP_SCRATCH_SIZE;
start = skb->data + hdr_len;
err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen);
if (err) {
goto error;
}
if ((dlen + sizeof(struct ipv6_comp_hdr)) >= plen) {
goto out_ok;
}
memcpy(start, scratch, dlen);
pskb_trim(skb, hdr_len+dlen);
/* insert ipcomp header and replace datagram */
tmp_iph = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_iph) {
err = -ENOMEM;
goto error;
}
memcpy(tmp_iph, skb->nh.raw, hdr_len);
top_iph = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6_comp_hdr));
memcpy(top_iph, tmp_iph, hdr_len);
kfree(tmp_iph);
top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
skb->nh.raw = skb->data; /* top_iph */
ip6_found_nexthdr(skb, &prevhdr);
*prevhdr = IPPROTO_COMP;
ipch = (struct ipv6_comp_hdr *)((unsigned char *)top_iph + hdr_len);
ipch->nexthdr = nexthdr;
ipch->flags = 0;
ipch->cpi = htons((u16 )ntohl(x->id.spi));
skb->h.raw = (unsigned char*)ipch;
out_ok:
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock_bh(&x->lock);
if ((skb->dst = dst_pop(dst)) == NULL) {
err = -EHOSTUNREACH;
goto error_nolock;
}
err = NET_XMIT_BYPASS;
out_exit:
return err;
error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(skb);
goto out_exit;
}
static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info)
{
u32 spi;
struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
struct ipv6_comp_hdr *ipcomph = (struct ipv6_comp_hdr*)(skb->data+offset);
struct xfrm_state *x;
if (type != ICMPV6_DEST_UNREACH || type != ICMPV6_PKT_TOOBIG)
return;
spi = ntohl(ntohs(ipcomph->cpi));
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
printk(KERN_DEBUG "pmtu discvovery on SA IPCOMP/%08x/"
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
spi, NIP6(iph->daddr));
xfrm_state_put(x);
}
static void ipcomp6_free_data(struct ipcomp_data *ipcd)
{
if (ipcd->tfm)
crypto_free_tfm(ipcd->tfm);
if (ipcd->scratch)
kfree(ipcd->scratch);
}
static void ipcomp6_destroy(struct xfrm_state *x)
{
struct ipcomp_data *ipcd = x->data;
ipcomp6_free_data(ipcd);
kfree(ipcd);
}
static int ipcomp6_init_state(struct xfrm_state *x, void *args)
{
int err = -ENOMEM;
struct ipcomp_data *ipcd;
struct xfrm_algo_desc *calg_desc;
ipcd = kmalloc(sizeof(*ipcd), GFP_KERNEL);
if (!ipcd)
goto error;
memset(ipcd, 0, sizeof(*ipcd));
x->props.header_len = sizeof(struct ipv6_comp_hdr);
if (x->props.mode)
x->props.header_len += sizeof(struct ipv6hdr);
x->data = ipcd;
ipcd->scratch = kmalloc(IPCOMP_SCRATCH_SIZE, GFP_KERNEL);
if (!ipcd->scratch)
goto error;
ipcd->tfm = crypto_alloc_tfm(x->calg->alg_name, 0);
if (!ipcd->tfm)
goto error;
calg_desc = xfrm_calg_get_byname(x->calg->alg_name);
BUG_ON(!calg_desc);
ipcd->threshold = calg_desc->uinfo.comp.threshold;
err = 0;
out:
return err;
error:
if (ipcd) {
ipcomp6_free_data(ipcd);
kfree(ipcd);
}
goto out;
}
static struct xfrm_type ipcomp6_type =
{
.description = "IPCOMP6",
.owner = THIS_MODULE,
.proto = IPPROTO_COMP,
.init_state = ipcomp6_init_state,
.destructor = ipcomp6_destroy,
.input = ipcomp6_input,
.output = ipcomp6_output,
};
static struct inet6_protocol ipcomp6_protocol =
{
.handler = xfrm6_rcv,
.err_handler = ipcomp6_err,
.flags = INET6_PROTO_NOPOLICY,
};
static int __init ipcomp6_init(void)
{
if (xfrm_register_type(&ipcomp6_type, AF_INET6) < 0) {
printk(KERN_INFO "ipcomp6 init: can't add xfrm type\n");
return -EAGAIN;
}
if (inet6_add_protocol(&ipcomp6_protocol, IPPROTO_COMP) < 0) {
printk(KERN_INFO "ipcomp6 init: can't add protocol\n");
xfrm_unregister_type(&ipcomp6_type, AF_INET6);
return -EAGAIN;
}
return 0;
}
static void __exit ipcomp6_fini(void)
{
if (inet6_del_protocol(&ipcomp6_protocol, IPPROTO_COMP) < 0)
printk(KERN_INFO "ipv6 ipcomp close: can't remove protocol\n");
if (xfrm_unregister_type(&ipcomp6_type, AF_INET6) < 0)
printk(KERN_INFO "ipv6 ipcomp close: can't remove xfrm type\n");
}
module_init(ipcomp6_init);
module_exit(ipcomp6_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) for IPv6 - RFC3173");
MODULE_AUTHOR("Mitsuru KANDA <mk@linux-ipv6.org>");
......@@ -35,5 +35,6 @@ EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(in6_dev_finish_destroy);
EXPORT_SYMBOL(ip6_found_nexthdr);
EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options);
......@@ -4,5 +4,5 @@
obj-$(CONFIG_IPX) += ipx.o
ipx-y := af_ipx.o ipx_proc.o
ipx-y := af_ipx.o ipx_route.o ipx_proc.o
ipx-$(CONFIG_SYSCTL) += sysctl_net_ipx.o
This diff is collapsed.
......@@ -5,6 +5,7 @@
*/
#include <linux/config.h>
#ifdef CONFIG_PROC_FS
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
......@@ -12,7 +13,6 @@
#include <linux/tcp.h>
#include <net/ipx.h>
#ifdef CONFIG_PROC_FS
static __inline__ struct ipx_interface *ipx_get_interface_idx(loff_t pos)
{
struct ipx_interface *i;
......
/*
* Implements the IPX routing routines.
* Code moved from af_ipx.c.
*
* Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2003
*
* See net/ipx/ChangeLog.
*/
#include <linux/config.h>
#include <linux/list.h>
#include <linux/route.h>
#include <linux/spinlock.h>
#include <net/ipx.h>
#include <net/sock.h>
LIST_HEAD(ipx_routes);
rwlock_t ipx_routes_lock = RW_LOCK_UNLOCKED;
extern struct ipx_interface *ipx_internal_net;
extern __u16 ipx_cksum(struct ipxhdr *packet, int length);
extern struct ipx_interface *ipxitf_find_using_net(__u32 net);
extern int ipxitf_demux_socket(struct ipx_interface *intrfc,
struct sk_buff *skb, int copy);
extern int ipxitf_demux_socket(struct ipx_interface *intrfc,
struct sk_buff *skb, int copy);
extern int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb,
char *node);
extern struct ipx_interface *ipxitf_find_using_net(__u32 net);
struct ipx_route *ipxrtr_lookup(__u32 net)
{
struct ipx_route *r;
read_lock_bh(&ipx_routes_lock);
list_for_each_entry(r, &ipx_routes, node)
if (r->ir_net == net) {
ipxrtr_hold(r);
goto unlock;
}
r = NULL;
unlock:
read_unlock_bh(&ipx_routes_lock);
return r;
}
/*
* Caller must hold a reference to intrfc
*/
int ipxrtr_add_route(__u32 network, struct ipx_interface *intrfc,
unsigned char *node)
{
struct ipx_route *rt;
int rc;
/* Get a route structure; either existing or create */
rt = ipxrtr_lookup(network);
if (!rt) {
rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
rc = -EAGAIN;
if (!rt)
goto out;
atomic_set(&rt->refcnt, 1);
ipxrtr_hold(rt);
write_lock_bh(&ipx_routes_lock);
list_add(&rt->node, &ipx_routes);
write_unlock_bh(&ipx_routes_lock);
} else {
rc = -EEXIST;
if (intrfc == ipx_internal_net)
goto out_put;
}
rt->ir_net = network;
rt->ir_intrfc = intrfc;
if (!node) {
memset(rt->ir_router_node, '\0', IPX_NODE_LEN);
rt->ir_routed = 0;
} else {
memcpy(rt->ir_router_node, node, IPX_NODE_LEN);
rt->ir_routed = 1;
}
rc = 0;
out_put:
ipxrtr_put(rt);
out:
return rc;
}
void ipxrtr_del_routes(struct ipx_interface *intrfc)
{
struct ipx_route *r, *tmp;
write_lock_bh(&ipx_routes_lock);
list_for_each_entry_safe(r, tmp, &ipx_routes, node)
if (r->ir_intrfc == intrfc) {
list_del(&r->node);
ipxrtr_put(r);
}
write_unlock_bh(&ipx_routes_lock);
}
static int ipxrtr_create(struct ipx_route_definition *rd)
{
struct ipx_interface *intrfc;
int rc = -ENETUNREACH;
/* Find the appropriate interface */
intrfc = ipxitf_find_using_net(rd->ipx_router_network);
if (!intrfc)
goto out;
rc = ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node);
ipxitf_put(intrfc);
out:
return rc;
}
static int ipxrtr_delete(long net)
{
struct ipx_route *r, *tmp;
int rc;
write_lock_bh(&ipx_routes_lock);
list_for_each_entry_safe(r, tmp, &ipx_routes, node)
if (r->ir_net == net) {
/* Directly connected; can't lose route */
rc = -EPERM;
if (!r->ir_routed)
goto out;
list_del(&r->node);
ipxrtr_put(r);
rc = 0;
goto out;
}
rc = -ENOENT;
out:
write_unlock_bh(&ipx_routes_lock);
return rc;
}
/*
* The skb has to be unshared, we'll end up calling ipxitf_send, that'll
* modify the packet
*/
int ipxrtr_route_skb(struct sk_buff *skb)
{
struct ipxhdr *ipx = ipx_hdr(skb);
struct ipx_route *r = ipxrtr_lookup(IPX_SKB_CB(skb)->ipx_dest_net);
if (!r) { /* no known route */
kfree_skb(skb);
return 0;
}
ipxitf_hold(r->ir_intrfc);
ipxitf_send(r->ir_intrfc, skb, r->ir_routed ?
r->ir_router_node : ipx->ipx_dest.node);
ipxitf_put(r->ir_intrfc);
ipxrtr_put(r);
return 0;
}
/*
* Route an outgoing frame from a socket.
*/
int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
struct iovec *iov, int len, int noblock)
{
struct sk_buff *skb;
struct ipx_opt *ipxs = ipx_sk(sk);
struct ipx_interface *intrfc;
struct ipxhdr *ipx;
int size;
int ipx_offset;
struct ipx_route *rt = NULL;
int rc;
/* Find the appropriate interface on which to send packet */
if (!usipx->sipx_network && ipx_primary_net) {
usipx->sipx_network = ipx_primary_net->if_netnum;
intrfc = ipx_primary_net;
} else {
rt = ipxrtr_lookup(usipx->sipx_network);
rc = -ENETUNREACH;
if (!rt)
goto out;
intrfc = rt->ir_intrfc;
}
ipxitf_hold(intrfc);
ipx_offset = intrfc->if_ipx_offset;
size = sizeof(struct ipxhdr) + len + ipx_offset;
skb = sock_alloc_send_skb(sk, size, noblock, &rc);
if (!skb)
goto out_put;
skb_reserve(skb, ipx_offset);
skb->sk = sk;
/* Fill in IPX header */
skb->h.raw = skb->nh.raw = skb_put(skb, sizeof(struct ipxhdr));
ipx = ipx_hdr(skb);
ipx->ipx_pktsize = htons(len + sizeof(struct ipxhdr));
IPX_SKB_CB(skb)->ipx_tctrl = 0;
ipx->ipx_type = usipx->sipx_type;
IPX_SKB_CB(skb)->last_hop.index = -1;
#ifdef CONFIG_IPX_INTERN
IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum;
memcpy(ipx->ipx_source.node, ipxs->node, IPX_NODE_LEN);
#else
rc = ntohs(ipxs->port);
if (rc == 0x453 || rc == 0x452) {
/* RIP/SAP special handling for mars_nwe */
IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN);
} else {
IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum;
memcpy(ipx->ipx_source.node, ipxs->intrfc->if_node,
IPX_NODE_LEN);
}
#endif /* CONFIG_IPX_INTERN */
ipx->ipx_source.sock = ipxs->port;
IPX_SKB_CB(skb)->ipx_dest_net = usipx->sipx_network;
memcpy(ipx->ipx_dest.node, usipx->sipx_node, IPX_NODE_LEN);
ipx->ipx_dest.sock = usipx->sipx_port;
rc = memcpy_fromiovec(skb_put(skb, len), iov, len);
if (rc) {
kfree_skb(skb);
goto out_put;
}
/* Apply checksum. Not allowed on 802.3 links. */
if (sk->no_check || intrfc->if_dlink_type == IPX_FRAME_8023)
ipx->ipx_checksum = 0xFFFF;
else
ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));
rc = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
rt->ir_router_node : ipx->ipx_dest.node);
out_put:
ipxitf_put(intrfc);
if (rt)
ipxrtr_put(rt);
out:
return rc;
}
/*
* We use a normal struct rtentry for route handling
*/
int ipxrtr_ioctl(unsigned int cmd, void *arg)
{
struct rtentry rt; /* Use these to behave like 'other' stacks */
struct sockaddr_ipx *sg, *st;
int rc = -EFAULT;
if (copy_from_user(&rt, arg, sizeof(rt)))
goto out;
sg = (struct sockaddr_ipx *)&rt.rt_gateway;
st = (struct sockaddr_ipx *)&rt.rt_dst;
rc = -EINVAL;
if (!(rt.rt_flags & RTF_GATEWAY) || /* Direct routes are fixed */
sg->sipx_family != AF_IPX ||
st->sipx_family != AF_IPX)
goto out;
switch (cmd) {
case SIOCDELRT:
rc = ipxrtr_delete(st->sipx_network);
break;
case SIOCADDRT: {
struct ipx_route_definition f;
f.ipx_network = st->sipx_network;
f.ipx_router_network = sg->sipx_network;
memcpy(f.ipx_router_node, sg->sipx_node, IPX_NODE_LEN);
rc = ipxrtr_create(&f);
break;
}
}
out:
return rc;
}
......@@ -34,7 +34,7 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
offset_seq = offsetof(struct ip_esp_hdr, seq_no);
break;
case IPPROTO_COMP:
if (!pskb_may_pull(skb, 4))
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
return -EINVAL;
*spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
*seq = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment