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