18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * History 48c2ecf20Sopenharmony_ci * 03-01-2007 Added forwarding for x.25 Andrew Hendry 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "X25: " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <net/x25.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ciLIST_HEAD(x25_forward_list); 158c2ecf20Sopenharmony_ciDEFINE_RWLOCK(x25_forward_list_lock); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciint x25_forward_call(struct x25_address *dest_addr, struct x25_neigh *from, 188c2ecf20Sopenharmony_ci struct sk_buff *skb, int lci) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct x25_route *rt; 218c2ecf20Sopenharmony_ci struct x25_neigh *neigh_new = NULL; 228c2ecf20Sopenharmony_ci struct list_head *entry; 238c2ecf20Sopenharmony_ci struct x25_forward *x25_frwd, *new_frwd; 248c2ecf20Sopenharmony_ci struct sk_buff *skbn; 258c2ecf20Sopenharmony_ci short same_lci = 0; 268c2ecf20Sopenharmony_ci int rc = 0; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if ((rt = x25_get_route(dest_addr)) == NULL) 298c2ecf20Sopenharmony_ci goto out_no_route; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if ((neigh_new = x25_get_neigh(rt->dev)) == NULL) { 328c2ecf20Sopenharmony_ci /* This shouldn't happen, if it occurs somehow 338c2ecf20Sopenharmony_ci * do something sensible 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci goto out_put_route; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* Avoid a loop. This is the normal exit path for a 398c2ecf20Sopenharmony_ci * system with only one x.25 iface and default route 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci if (rt->dev == from->dev) { 428c2ecf20Sopenharmony_ci goto out_put_nb; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Remote end sending a call request on an already 468c2ecf20Sopenharmony_ci * established LCI? It shouldn't happen, just in case.. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci read_lock_bh(&x25_forward_list_lock); 498c2ecf20Sopenharmony_ci list_for_each(entry, &x25_forward_list) { 508c2ecf20Sopenharmony_ci x25_frwd = list_entry(entry, struct x25_forward, node); 518c2ecf20Sopenharmony_ci if (x25_frwd->lci == lci) { 528c2ecf20Sopenharmony_ci pr_warn("call request for lci which is already registered!, transmitting but not registering new pair\n"); 538c2ecf20Sopenharmony_ci same_lci = 1; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci read_unlock_bh(&x25_forward_list_lock); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Save the forwarding details for future traffic */ 598c2ecf20Sopenharmony_ci if (!same_lci){ 608c2ecf20Sopenharmony_ci if ((new_frwd = kmalloc(sizeof(struct x25_forward), 618c2ecf20Sopenharmony_ci GFP_ATOMIC)) == NULL){ 628c2ecf20Sopenharmony_ci rc = -ENOMEM; 638c2ecf20Sopenharmony_ci goto out_put_nb; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci new_frwd->lci = lci; 668c2ecf20Sopenharmony_ci new_frwd->dev1 = rt->dev; 678c2ecf20Sopenharmony_ci new_frwd->dev2 = from->dev; 688c2ecf20Sopenharmony_ci write_lock_bh(&x25_forward_list_lock); 698c2ecf20Sopenharmony_ci list_add(&new_frwd->node, &x25_forward_list); 708c2ecf20Sopenharmony_ci write_unlock_bh(&x25_forward_list_lock); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Forward the call request */ 748c2ecf20Sopenharmony_ci if ( (skbn = skb_clone(skb, GFP_ATOMIC)) == NULL){ 758c2ecf20Sopenharmony_ci goto out_put_nb; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci x25_transmit_link(skbn, neigh_new); 788c2ecf20Sopenharmony_ci rc = 1; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciout_put_nb: 828c2ecf20Sopenharmony_ci x25_neigh_put(neigh_new); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciout_put_route: 858c2ecf20Sopenharmony_ci x25_route_put(rt); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciout_no_route: 888c2ecf20Sopenharmony_ci return rc; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciint x25_forward_data(int lci, struct x25_neigh *from, struct sk_buff *skb) { 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci struct x25_forward *frwd; 958c2ecf20Sopenharmony_ci struct list_head *entry; 968c2ecf20Sopenharmony_ci struct net_device *peer = NULL; 978c2ecf20Sopenharmony_ci struct x25_neigh *nb; 988c2ecf20Sopenharmony_ci struct sk_buff *skbn; 998c2ecf20Sopenharmony_ci int rc = 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci read_lock_bh(&x25_forward_list_lock); 1028c2ecf20Sopenharmony_ci list_for_each(entry, &x25_forward_list) { 1038c2ecf20Sopenharmony_ci frwd = list_entry(entry, struct x25_forward, node); 1048c2ecf20Sopenharmony_ci if (frwd->lci == lci) { 1058c2ecf20Sopenharmony_ci /* The call is established, either side can send */ 1068c2ecf20Sopenharmony_ci if (from->dev == frwd->dev1) { 1078c2ecf20Sopenharmony_ci peer = frwd->dev2; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci peer = frwd->dev1; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci read_unlock_bh(&x25_forward_list_lock); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if ( (nb = x25_get_neigh(peer)) == NULL) 1178c2ecf20Sopenharmony_ci goto out; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if ( (skbn = pskb_copy(skb, GFP_ATOMIC)) == NULL){ 1208c2ecf20Sopenharmony_ci goto output; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci x25_transmit_link(skbn, nb); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci rc = 1; 1268c2ecf20Sopenharmony_cioutput: 1278c2ecf20Sopenharmony_ci x25_neigh_put(nb); 1288c2ecf20Sopenharmony_ciout: 1298c2ecf20Sopenharmony_ci return rc; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid x25_clear_forward_by_lci(unsigned int lci) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct x25_forward *fwd, *tmp; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci write_lock_bh(&x25_forward_list_lock); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci list_for_each_entry_safe(fwd, tmp, &x25_forward_list, node) { 1398c2ecf20Sopenharmony_ci if (fwd->lci == lci) { 1408c2ecf20Sopenharmony_ci list_del(&fwd->node); 1418c2ecf20Sopenharmony_ci kfree(fwd); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci write_unlock_bh(&x25_forward_list_lock); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid x25_clear_forward_by_dev(struct net_device *dev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct x25_forward *fwd, *tmp; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci write_lock_bh(&x25_forward_list_lock); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci list_for_each_entry_safe(fwd, tmp, &x25_forward_list, node) { 1558c2ecf20Sopenharmony_ci if ((fwd->dev1 == dev) || (fwd->dev2 == dev)){ 1568c2ecf20Sopenharmony_ci list_del(&fwd->node); 1578c2ecf20Sopenharmony_ci kfree(fwd); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci write_unlock_bh(&x25_forward_list_lock); 1618c2ecf20Sopenharmony_ci} 162