xref: /openbmc/linux/net/x25/x25_forward.c (revision 96bffe70)
1ee5d8f4dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
295a9dc43SAndrew Hendry /*
395a9dc43SAndrew Hendry  *	History
495a9dc43SAndrew Hendry  *	03-01-2007	Added forwarding for x.25	Andrew Hendry
595a9dc43SAndrew Hendry  */
6b73e9e3cSwangweidong 
7b73e9e3cSwangweidong #define pr_fmt(fmt) "X25: " fmt
8b73e9e3cSwangweidong 
995a9dc43SAndrew Hendry #include <linux/if_arp.h>
1095a9dc43SAndrew Hendry #include <linux/init.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
1295a9dc43SAndrew Hendry #include <net/x25.h>
1395a9dc43SAndrew Hendry 
140e3cf7e9SDenis Cheng LIST_HEAD(x25_forward_list);
1595a9dc43SAndrew Hendry DEFINE_RWLOCK(x25_forward_list_lock);
1695a9dc43SAndrew Hendry 
x25_forward_call(struct x25_address * dest_addr,struct x25_neigh * from,struct sk_buff * skb,int lci)1795a9dc43SAndrew Hendry int x25_forward_call(struct x25_address *dest_addr, struct x25_neigh *from,
1895a9dc43SAndrew Hendry 			struct sk_buff *skb, int lci)
1995a9dc43SAndrew Hendry {
2095a9dc43SAndrew Hendry 	struct x25_route *rt;
2195a9dc43SAndrew Hendry 	struct x25_neigh *neigh_new = NULL;
2295a9dc43SAndrew Hendry 	struct x25_forward *x25_frwd, *new_frwd;
2395a9dc43SAndrew Hendry 	struct sk_buff *skbn;
2495a9dc43SAndrew Hendry 	short same_lci = 0;
2595a9dc43SAndrew Hendry 	int rc = 0;
2695a9dc43SAndrew Hendry 
27e4ce837dSAdrian Bunk 	if ((rt = x25_get_route(dest_addr)) == NULL)
28e4ce837dSAdrian Bunk 		goto out_no_route;
2995a9dc43SAndrew Hendry 
3095a9dc43SAndrew Hendry 	if ((neigh_new = x25_get_neigh(rt->dev)) == NULL) {
3125985edcSLucas De Marchi 		/* This shouldn't happen, if it occurs somehow
3295a9dc43SAndrew Hendry 		 * do something sensible
3395a9dc43SAndrew Hendry 		 */
3495a9dc43SAndrew Hendry 		goto out_put_route;
3595a9dc43SAndrew Hendry 	}
3695a9dc43SAndrew Hendry 
3795a9dc43SAndrew Hendry 	/* Avoid a loop. This is the normal exit path for a
3895a9dc43SAndrew Hendry 	 * system with only one x.25 iface and default route
3995a9dc43SAndrew Hendry 	 */
4095a9dc43SAndrew Hendry 	if (rt->dev == from->dev) {
4195a9dc43SAndrew Hendry 		goto out_put_nb;
4295a9dc43SAndrew Hendry 	}
4395a9dc43SAndrew Hendry 
4495a9dc43SAndrew Hendry 	/* Remote end sending a call request on an already
4525985edcSLucas De Marchi 	 * established LCI? It shouldn't happen, just in case..
4695a9dc43SAndrew Hendry 	 */
4795a9dc43SAndrew Hendry 	read_lock_bh(&x25_forward_list_lock);
48*96bffe70SWang Hai 	list_for_each_entry(x25_frwd, &x25_forward_list, node) {
4995a9dc43SAndrew Hendry 		if (x25_frwd->lci == lci) {
50b73e9e3cSwangweidong 			pr_warn("call request for lci which is already registered!, transmitting but not registering new pair\n");
5195a9dc43SAndrew Hendry 			same_lci = 1;
5295a9dc43SAndrew Hendry 		}
5395a9dc43SAndrew Hendry 	}
5495a9dc43SAndrew Hendry 	read_unlock_bh(&x25_forward_list_lock);
5595a9dc43SAndrew Hendry 
5695a9dc43SAndrew Hendry 	/* Save the forwarding details for future traffic */
5795a9dc43SAndrew Hendry 	if (!same_lci){
5895a9dc43SAndrew Hendry 		if ((new_frwd = kmalloc(sizeof(struct x25_forward),
5995a9dc43SAndrew Hendry 						GFP_ATOMIC)) == NULL){
6095a9dc43SAndrew Hendry 			rc = -ENOMEM;
6195a9dc43SAndrew Hendry 			goto out_put_nb;
6295a9dc43SAndrew Hendry 		}
6395a9dc43SAndrew Hendry 		new_frwd->lci = lci;
6495a9dc43SAndrew Hendry 		new_frwd->dev1 = rt->dev;
6595a9dc43SAndrew Hendry 		new_frwd->dev2 = from->dev;
6695a9dc43SAndrew Hendry 		write_lock_bh(&x25_forward_list_lock);
6795a9dc43SAndrew Hendry 		list_add(&new_frwd->node, &x25_forward_list);
6895a9dc43SAndrew Hendry 		write_unlock_bh(&x25_forward_list_lock);
6995a9dc43SAndrew Hendry 	}
7095a9dc43SAndrew Hendry 
7195a9dc43SAndrew Hendry 	/* Forward the call request */
7295a9dc43SAndrew Hendry 	if ( (skbn = skb_clone(skb, GFP_ATOMIC)) == NULL){
7395a9dc43SAndrew Hendry 		goto out_put_nb;
7495a9dc43SAndrew Hendry 	}
7595a9dc43SAndrew Hendry 	x25_transmit_link(skbn, neigh_new);
7695a9dc43SAndrew Hendry 	rc = 1;
7795a9dc43SAndrew Hendry 
7895a9dc43SAndrew Hendry 
7995a9dc43SAndrew Hendry out_put_nb:
8095a9dc43SAndrew Hendry 	x25_neigh_put(neigh_new);
8195a9dc43SAndrew Hendry 
8295a9dc43SAndrew Hendry out_put_route:
8395a9dc43SAndrew Hendry 	x25_route_put(rt);
84e4ce837dSAdrian Bunk 
85e4ce837dSAdrian Bunk out_no_route:
8695a9dc43SAndrew Hendry 	return rc;
8795a9dc43SAndrew Hendry }
8895a9dc43SAndrew Hendry 
8995a9dc43SAndrew Hendry 
x25_forward_data(int lci,struct x25_neigh * from,struct sk_buff * skb)9095a9dc43SAndrew Hendry int x25_forward_data(int lci, struct x25_neigh *from, struct sk_buff *skb) {
9195a9dc43SAndrew Hendry 
9295a9dc43SAndrew Hendry 	struct x25_forward *frwd;
9395a9dc43SAndrew Hendry 	struct net_device *peer = NULL;
9495a9dc43SAndrew Hendry 	struct x25_neigh *nb;
9595a9dc43SAndrew Hendry 	struct sk_buff *skbn;
9695a9dc43SAndrew Hendry 	int rc = 0;
9795a9dc43SAndrew Hendry 
9895a9dc43SAndrew Hendry 	read_lock_bh(&x25_forward_list_lock);
99*96bffe70SWang Hai 	list_for_each_entry(frwd, &x25_forward_list, node) {
10095a9dc43SAndrew Hendry 		if (frwd->lci == lci) {
10195a9dc43SAndrew Hendry 			/* The call is established, either side can send */
10295a9dc43SAndrew Hendry 			if (from->dev == frwd->dev1) {
10395a9dc43SAndrew Hendry 				peer = frwd->dev2;
10495a9dc43SAndrew Hendry 			} else {
10595a9dc43SAndrew Hendry 				peer = frwd->dev1;
10695a9dc43SAndrew Hendry 			}
10795a9dc43SAndrew Hendry 			break;
10895a9dc43SAndrew Hendry 		}
10995a9dc43SAndrew Hendry 	}
11095a9dc43SAndrew Hendry 	read_unlock_bh(&x25_forward_list_lock);
11195a9dc43SAndrew Hendry 
11295a9dc43SAndrew Hendry 	if ( (nb = x25_get_neigh(peer)) == NULL)
11395a9dc43SAndrew Hendry 		goto out;
11495a9dc43SAndrew Hendry 
11595a9dc43SAndrew Hendry 	if ( (skbn = pskb_copy(skb, GFP_ATOMIC)) == NULL){
11676975f8aSJulia Lawall 		goto output;
11795a9dc43SAndrew Hendry 
11895a9dc43SAndrew Hendry 	}
11995a9dc43SAndrew Hendry 	x25_transmit_link(skbn, nb);
12095a9dc43SAndrew Hendry 
12195a9dc43SAndrew Hendry 	rc = 1;
12276975f8aSJulia Lawall output:
12376975f8aSJulia Lawall 	x25_neigh_put(nb);
12495a9dc43SAndrew Hendry out:
12595a9dc43SAndrew Hendry 	return rc;
12695a9dc43SAndrew Hendry }
12795a9dc43SAndrew Hendry 
x25_clear_forward_by_lci(unsigned int lci)12895a9dc43SAndrew Hendry void x25_clear_forward_by_lci(unsigned int lci)
12995a9dc43SAndrew Hendry {
1301e5946f5Schenqiwu 	struct x25_forward *fwd, *tmp;
13195a9dc43SAndrew Hendry 
13295a9dc43SAndrew Hendry 	write_lock_bh(&x25_forward_list_lock);
13395a9dc43SAndrew Hendry 
1341e5946f5Schenqiwu 	list_for_each_entry_safe(fwd, tmp, &x25_forward_list, node) {
13595a9dc43SAndrew Hendry 		if (fwd->lci == lci) {
13695a9dc43SAndrew Hendry 			list_del(&fwd->node);
13795a9dc43SAndrew Hendry 			kfree(fwd);
13895a9dc43SAndrew Hendry 		}
13995a9dc43SAndrew Hendry 	}
14095a9dc43SAndrew Hendry 	write_unlock_bh(&x25_forward_list_lock);
14195a9dc43SAndrew Hendry }
14295a9dc43SAndrew Hendry 
14395a9dc43SAndrew Hendry 
x25_clear_forward_by_dev(struct net_device * dev)14495a9dc43SAndrew Hendry void x25_clear_forward_by_dev(struct net_device *dev)
14595a9dc43SAndrew Hendry {
1461e5946f5Schenqiwu 	struct x25_forward *fwd, *tmp;
14795a9dc43SAndrew Hendry 
14895a9dc43SAndrew Hendry 	write_lock_bh(&x25_forward_list_lock);
14995a9dc43SAndrew Hendry 
1501e5946f5Schenqiwu 	list_for_each_entry_safe(fwd, tmp, &x25_forward_list, node) {
15195a9dc43SAndrew Hendry 		if ((fwd->dev1 == dev) || (fwd->dev2 == dev)){
15295a9dc43SAndrew Hendry 			list_del(&fwd->node);
15395a9dc43SAndrew Hendry 			kfree(fwd);
15495a9dc43SAndrew Hendry 		}
15595a9dc43SAndrew Hendry 	}
15695a9dc43SAndrew Hendry 	write_unlock_bh(&x25_forward_list_lock);
15795a9dc43SAndrew Hendry }
158