1/* 2 * Implements the IPX routing routines. 3 * Code moved from af_ipx.c. 4 * 5 * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2003 6 * 7 * See net/ipx/ChangeLog. 8 */ 9 10#include <linux/list.h> 11#include <linux/route.h> 12#include <linux/slab.h> 13#include <linux/spinlock.h> 14 15#include <net/ipx.h> 16#include <net/sock.h> 17 18LIST_HEAD(ipx_routes); 19DEFINE_RWLOCK(ipx_routes_lock); 20 21extern struct ipx_interface *ipx_internal_net; 22 23extern __be16 ipx_cksum(struct ipxhdr *packet, int length); 24extern struct ipx_interface *ipxitf_find_using_net(__be32 net); 25extern int ipxitf_demux_socket(struct ipx_interface *intrfc, 26 struct sk_buff *skb, int copy); 27extern int ipxitf_demux_socket(struct ipx_interface *intrfc, 28 struct sk_buff *skb, int copy); 29extern int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, 30 char *node); 31extern struct ipx_interface *ipxitf_find_using_net(__be32 net); 32 33struct ipx_route *ipxrtr_lookup(__be32 net) 34{ 35 struct ipx_route *r; 36 37 read_lock_bh(&ipx_routes_lock); 38 list_for_each_entry(r, &ipx_routes, node) 39 if (r->ir_net == net) { 40 ipxrtr_hold(r); 41 goto unlock; 42 } 43 r = NULL; 44unlock: 45 read_unlock_bh(&ipx_routes_lock); 46 return r; 47} 48 49/* 50 * Caller must hold a reference to intrfc 51 */ 52int ipxrtr_add_route(__be32 network, struct ipx_interface *intrfc, 53 unsigned char *node) 54{ 55 struct ipx_route *rt; 56 int rc; 57 58 /* Get a route structure; either existing or create */ 59 rt = ipxrtr_lookup(network); 60 if (!rt) { 61 rt = kmalloc(sizeof(*rt), GFP_ATOMIC); 62 rc = -EAGAIN; 63 if (!rt) 64 goto out; 65 66 atomic_set(&rt->refcnt, 1); 67 ipxrtr_hold(rt); 68 write_lock_bh(&ipx_routes_lock); 69 list_add(&rt->node, &ipx_routes); 70 write_unlock_bh(&ipx_routes_lock); 71 } else { 72 rc = -EEXIST; 73 if (intrfc == ipx_internal_net) 74 goto out_put; 75 } 76 77 rt->ir_net = network; 78 rt->ir_intrfc = intrfc; 79 if (!node) { 80 memset(rt->ir_router_node, '\0', IPX_NODE_LEN); 81 rt->ir_routed = 0; 82 } else { 83 memcpy(rt->ir_router_node, node, IPX_NODE_LEN); 84 rt->ir_routed = 1; 85 } 86 87 rc = 0; 88out_put: 89 ipxrtr_put(rt); 90out: 91 return rc; 92} 93 94void ipxrtr_del_routes(struct ipx_interface *intrfc) 95{ 96 struct ipx_route *r, *tmp; 97 98 write_lock_bh(&ipx_routes_lock); 99 list_for_each_entry_safe(r, tmp, &ipx_routes, node) 100 if (r->ir_intrfc == intrfc) { 101 list_del(&r->node); 102 ipxrtr_put(r); 103 } 104 write_unlock_bh(&ipx_routes_lock); 105} 106 107static int ipxrtr_create(struct ipx_route_definition *rd) 108{ 109 struct ipx_interface *intrfc; 110 int rc = -ENETUNREACH; 111 112 /* Find the appropriate interface */ 113 intrfc = ipxitf_find_using_net(rd->ipx_router_network); 114 if (!intrfc) 115 goto out; 116 rc = ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node); 117 ipxitf_put(intrfc); 118out: 119 return rc; 120} 121 122static int ipxrtr_delete(__be32 net) 123{ 124 struct ipx_route *r, *tmp; 125 int rc; 126 127 write_lock_bh(&ipx_routes_lock); 128 list_for_each_entry_safe(r, tmp, &ipx_routes, node) 129 if (r->ir_net == net) { 130 /* Directly connected; can't lose route */ 131 rc = -EPERM; 132 if (!r->ir_routed) 133 goto out; 134 list_del(&r->node); 135 ipxrtr_put(r); 136 rc = 0; 137 goto out; 138 } 139 rc = -ENOENT; 140out: 141 write_unlock_bh(&ipx_routes_lock); 142 return rc; 143} 144 145/* 146 * The skb has to be unshared, we'll end up calling ipxitf_send, that'll 147 * modify the packet 148 */ 149int ipxrtr_route_skb(struct sk_buff *skb) 150{ 151 struct ipxhdr *ipx = ipx_hdr(skb); 152 struct ipx_route *r = ipxrtr_lookup(IPX_SKB_CB(skb)->ipx_dest_net); 153 154 if (!r) { /* no known route */ 155 kfree_skb(skb); 156 return 0; 157 } 158 159 ipxitf_hold(r->ir_intrfc); 160 ipxitf_send(r->ir_intrfc, skb, r->ir_routed ? 161 r->ir_router_node : ipx->ipx_dest.node); 162 ipxitf_put(r->ir_intrfc); 163 ipxrtr_put(r); 164 165 return 0; 166} 167 168/* 169 * Route an outgoing frame from a socket. 170 */ 171int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, 172 struct iovec *iov, size_t len, int noblock) 173{ 174 struct sk_buff *skb; 175 struct ipx_sock *ipxs = ipx_sk(sk); 176 struct ipx_interface *intrfc; 177 struct ipxhdr *ipx; 178 size_t size; 179 int ipx_offset; 180 struct ipx_route *rt = NULL; 181 int rc; 182 183 /* Find the appropriate interface on which to send packet */ 184 if (!usipx->sipx_network && ipx_primary_net) { 185 usipx->sipx_network = ipx_primary_net->if_netnum; 186 intrfc = ipx_primary_net; 187 } else { 188 rt = ipxrtr_lookup(usipx->sipx_network); 189 rc = -ENETUNREACH; 190 if (!rt) 191 goto out; 192 intrfc = rt->ir_intrfc; 193 } 194 195 ipxitf_hold(intrfc); 196 ipx_offset = intrfc->if_ipx_offset; 197 size = sizeof(struct ipxhdr) + len + ipx_offset; 198 199 skb = sock_alloc_send_skb(sk, size, noblock, &rc); 200 if (!skb) 201 goto out_put; 202 203 skb_reserve(skb, ipx_offset); 204 skb->sk = sk; 205 206 /* Fill in IPX header */ 207 skb_reset_network_header(skb); 208 skb_reset_transport_header(skb); 209 skb_put(skb, sizeof(struct ipxhdr)); 210 ipx = ipx_hdr(skb); 211 ipx->ipx_pktsize = htons(len + sizeof(struct ipxhdr)); 212 IPX_SKB_CB(skb)->ipx_tctrl = 0; 213 ipx->ipx_type = usipx->sipx_type; 214 215 IPX_SKB_CB(skb)->last_hop.index = -1; 216#ifdef CONFIG_IPX_INTERN 217 IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum; 218 memcpy(ipx->ipx_source.node, ipxs->node, IPX_NODE_LEN); 219#else 220 rc = ntohs(ipxs->port); 221 if (rc == 0x453 || rc == 0x452) { 222 /* RIP/SAP special handling for mars_nwe */ 223 IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum; 224 memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN); 225 } else { 226 IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum; 227 memcpy(ipx->ipx_source.node, ipxs->intrfc->if_node, 228 IPX_NODE_LEN); 229 } 230#endif /* CONFIG_IPX_INTERN */ 231 ipx->ipx_source.sock = ipxs->port; 232 IPX_SKB_CB(skb)->ipx_dest_net = usipx->sipx_network; 233 memcpy(ipx->ipx_dest.node, usipx->sipx_node, IPX_NODE_LEN); 234 ipx->ipx_dest.sock = usipx->sipx_port; 235 236 rc = memcpy_fromiovec(skb_put(skb, len), iov, len); 237 if (rc) { 238 kfree_skb(skb); 239 goto out_put; 240 } 241 242 /* Apply checksum. Not allowed on 802.3 links. */ 243 if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023)) 244 ipx->ipx_checksum = htons(0xFFFF); 245 else 246 ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr)); 247 248 rc = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? 249 rt->ir_router_node : ipx->ipx_dest.node); 250out_put: 251 ipxitf_put(intrfc); 252 if (rt) 253 ipxrtr_put(rt); 254out: 255 return rc; 256} 257 258/* 259 * We use a normal struct rtentry for route handling 260 */ 261int ipxrtr_ioctl(unsigned int cmd, void __user *arg) 262{ 263 struct rtentry rt; /* Use these to behave like 'other' stacks */ 264 struct sockaddr_ipx *sg, *st; 265 int rc = -EFAULT; 266 267 if (copy_from_user(&rt, arg, sizeof(rt))) 268 goto out; 269 270 sg = (struct sockaddr_ipx *)&rt.rt_gateway; 271 st = (struct sockaddr_ipx *)&rt.rt_dst; 272 273 rc = -EINVAL; 274 if (!(rt.rt_flags & RTF_GATEWAY) || /* Direct routes are fixed */ 275 sg->sipx_family != AF_IPX || 276 st->sipx_family != AF_IPX) 277 goto out; 278 279 switch (cmd) { 280 case SIOCDELRT: 281 rc = ipxrtr_delete(st->sipx_network); 282 break; 283 case SIOCADDRT: { 284 struct ipx_route_definition f; 285 f.ipx_network = st->sipx_network; 286 f.ipx_router_network = sg->sipx_network; 287 memcpy(f.ipx_router_node, sg->sipx_node, IPX_NODE_LEN); 288 rc = ipxrtr_create(&f); 289 break; 290 } 291 } 292 293out: 294 return rc; 295} 296