o ipx: move route functions to net/ipx/ipx_route.c

parent 910d5f2d
...@@ -125,4 +125,36 @@ extern void ipx_proc_exit(void); ...@@ -125,4 +125,36 @@ extern void ipx_proc_exit(void);
extern const char *ipx_frame_name(unsigned short); extern const char *ipx_frame_name(unsigned short);
extern const char *ipx_device_name(struct ipx_interface *intrfc); 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_ */ #endif /* _NET_INET_IPX_H_ */
...@@ -4,5 +4,5 @@ ...@@ -4,5 +4,5 @@
obj-$(CONFIG_IPX) += ipx.o 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 ipx-$(CONFIG_SYSCTL) += sysctl_net_ipx.o
...@@ -29,35 +29,31 @@ ...@@ -29,35 +29,31 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/if_arp.h>
#include <linux/socket.h> #include <linux/if_ether.h>
#include <linux/in.h> #include <linux/init.h>
#include <linux/ipx.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/list.h>
#include <linux/timer.h> #include <linux/module.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h> #include <linux/net.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <net/ipx.h> #include <linux/uio.h>
#include <linux/inet.h> #include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/string.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/route.h> #include <linux/types.h>
#include <net/sock.h> #include <linux/termios.h>
#include <asm/uaccess.h>
#include <asm/system.h> #include <net/ipx.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/termios.h> /* For TIOCOUTQ/INQ */
#include <linux/interrupt.h>
#include <net/p8022.h> #include <net/p8022.h>
#include <net/psnap.h> #include <net/psnap.h>
#include <linux/proc_fs.h> #include <net/sock.h>
#include <linux/stat.h>
#include <linux/init.h> #include <asm/uaccess.h>
#include <linux/if_arp.h>
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
extern void ipx_register_sysctl(void); extern void ipx_register_sysctl(void);
...@@ -81,14 +77,20 @@ static struct datalink_proto *pSNAP_datalink; ...@@ -81,14 +77,20 @@ static struct datalink_proto *pSNAP_datalink;
static struct proto_ops ipx_dgram_ops; static struct proto_ops ipx_dgram_ops;
LIST_HEAD(ipx_routes);
rwlock_t ipx_routes_lock = RW_LOCK_UNLOCKED;
LIST_HEAD(ipx_interfaces); LIST_HEAD(ipx_interfaces);
spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED; spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED;
struct ipx_interface *ipx_primary_net; struct ipx_interface *ipx_primary_net;
static struct ipx_interface *ipx_internal_net; struct ipx_interface *ipx_internal_net;
extern int ipxrtr_add_route(__u32 network, struct ipx_interface *intrfc,
unsigned char *node);
extern void ipxrtr_del_routes(struct ipx_interface *intrfc);
extern int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
struct iovec *iov, int len, int noblock);
extern int ipxrtr_route_skb(struct sk_buff *skb);
extern struct ipx_route *ipxrtr_lookup(__u32 net);
extern int ipxrtr_ioctl(unsigned int cmd, void *arg);
#undef IPX_REFCNT_DEBUG #undef IPX_REFCNT_DEBUG
#ifdef IPX_REFCNT_DEBUG #ifdef IPX_REFCNT_DEBUG
...@@ -122,28 +124,6 @@ static int ipxcfg_get_config_data(struct ipx_config_data *arg) ...@@ -122,28 +124,6 @@ static int ipxcfg_get_config_data(struct ipx_config_data *arg)
return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0; return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0;
} }
/* Handlers for the socket list. */
static __inline__ void ipxitf_hold(struct ipx_interface *intrfc)
{
atomic_inc(&intrfc->refcnt);
}
static 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 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);
}
/* /*
* Note: Sockets may not be removed _during_ an interrupt or inet_bh * Note: Sockets may not be removed _during_ an interrupt or inet_bh
* handler using this technique. They can be added although we do not * handler using this technique. They can be added although we do not
...@@ -201,7 +181,6 @@ static void ipx_destroy_socket(struct sock *sk) ...@@ -201,7 +181,6 @@ static void ipx_destroy_socket(struct sock *sk)
* The following code is used to support IPX Interfaces (IPXITF). An * The following code is used to support IPX Interfaces (IPXITF). An
* IPX interface is defined by a physical device and a frame type. * IPX interface is defined by a physical device and a frame type.
*/ */
static struct ipx_route *ipxrtr_lookup(__u32 net);
/* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */ /* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */
...@@ -238,7 +217,7 @@ static struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev, ...@@ -238,7 +217,7 @@ static struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev,
return i; return i;
} }
static struct ipx_interface *ipxitf_find_using_net(__u32 net) struct ipx_interface *ipxitf_find_using_net(__u32 net)
{ {
struct ipx_interface *i; struct ipx_interface *i;
...@@ -333,9 +312,7 @@ static struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc, ...@@ -333,9 +312,7 @@ static struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc,
} }
#endif #endif
static void ipxrtr_del_routes(struct ipx_interface *intrfc); void __ipxitf_down(struct ipx_interface *intrfc)
static void __ipxitf_down(struct ipx_interface *intrfc)
{ {
struct sock *s, *t; struct sock *s, *t;
...@@ -374,7 +351,7 @@ static void __ipxitf_down(struct ipx_interface *intrfc) ...@@ -374,7 +351,7 @@ static void __ipxitf_down(struct ipx_interface *intrfc)
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
static void ipxitf_down(struct ipx_interface *intrfc) void ipxitf_down(struct ipx_interface *intrfc)
{ {
spin_lock_bh(&ipx_interfaces_lock); spin_lock_bh(&ipx_interfaces_lock);
__ipxitf_down(intrfc); __ipxitf_down(intrfc);
...@@ -612,9 +589,7 @@ static struct sk_buff *ipxitf_adjust_skbuff(struct ipx_interface *intrfc, ...@@ -612,9 +589,7 @@ static struct sk_buff *ipxitf_adjust_skbuff(struct ipx_interface *intrfc,
} }
/* caller must hold a reference to intrfc and the skb has to be unshared */ /* caller must hold a reference to intrfc and the skb has to be unshared */
int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node)
static int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb,
char *node)
{ {
struct ipxhdr *ipx = ipx_hdr(skb); struct ipxhdr *ipx = ipx_hdr(skb);
struct net_device *dev = intrfc->if_dev; struct net_device *dev = intrfc->if_dev;
...@@ -720,9 +695,6 @@ static int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, ...@@ -720,9 +695,6 @@ static int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb,
return 0; return 0;
} }
static int ipxrtr_add_route(__u32 network, struct ipx_interface *intrfc,
unsigned char *);
static int ipxitf_add_local_route(struct ipx_interface *intrfc) static int ipxitf_add_local_route(struct ipx_interface *intrfc)
{ {
return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL); return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
...@@ -731,7 +703,6 @@ static int ipxitf_add_local_route(struct ipx_interface *intrfc) ...@@ -731,7 +703,6 @@ static int ipxitf_add_local_route(struct ipx_interface *intrfc)
static void ipxitf_discover_netnum(struct ipx_interface *intrfc, static void ipxitf_discover_netnum(struct ipx_interface *intrfc,
struct sk_buff *skb); struct sk_buff *skb);
static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb); static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb);
static int ipxrtr_route_skb(struct sk_buff *skb);
static int ipxitf_rcv(struct ipx_interface *intrfc, struct sk_buff *skb) static int ipxitf_rcv(struct ipx_interface *intrfc, struct sk_buff *skb)
{ {
...@@ -1261,130 +1232,6 @@ static int ipxitf_ioctl(unsigned int cmd, void *arg) ...@@ -1261,130 +1232,6 @@ static int ipxitf_ioctl(unsigned int cmd, void *arg)
return rc; return rc;
} }
/* Routing tables for the IPX socket layer. */
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);
}
static 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 */
static 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;
}
static 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;
}
/* /*
* Checksum routine for IPX * Checksum routine for IPX
*/ */
...@@ -1392,7 +1239,7 @@ static int ipxrtr_delete(long net) ...@@ -1392,7 +1239,7 @@ static int ipxrtr_delete(long net)
/* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */ /* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
/* This functions should *not* mess with packet contents */ /* This functions should *not* mess with packet contents */
static __u16 ipx_cksum(struct ipxhdr *packet, int length) __u16 ipx_cksum(struct ipxhdr *packet, int length)
{ {
/* /*
* NOTE: sum is a net byte order quantity, which optimizes the * NOTE: sum is a net byte order quantity, which optimizes the
...@@ -1424,154 +1271,6 @@ static __u16 ipx_cksum(struct ipxhdr *packet, int length) ...@@ -1424,154 +1271,6 @@ static __u16 ipx_cksum(struct ipxhdr *packet, int length)
return ~sum; return ~sum;
} }
/*
* Route an outgoing frame from a socket.
*/
static 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;
}
/* 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;
}
/*
* We use a normal struct rtentry for route handling
*/
static 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;
}
const char *ipx_frame_name(unsigned short frame) const char *ipx_frame_name(unsigned short frame)
{ {
char* rc = "None"; char* rc = "None";
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#ifdef CONFIG_PROC_FS
#include <linux/init.h> #include <linux/init.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -12,7 +13,6 @@ ...@@ -12,7 +13,6 @@
#include <linux/tcp.h> #include <linux/tcp.h>
#include <net/ipx.h> #include <net/ipx.h>
#ifdef CONFIG_PROC_FS
static __inline__ struct ipx_interface *ipx_get_interface_idx(loff_t pos) static __inline__ struct ipx_interface *ipx_get_interface_idx(loff_t pos)
{ {
struct ipx_interface *i; 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;
}
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