diff -rcN linux-source-2.6.32_clean/CREDITS linux-source-2.6.32/CREDITS *** linux-source-2.6.32_clean/CREDITS 2009-12-03 14:51:21.000000000 +1100 --- linux-source-2.6.32/CREDITS 2012-12-14 17:00:59.000000000 +1100 *************** *** 3615,3620 **** --- 3615,3621 ---- W: http://www.chello.nl/~j.vreeken/ D: SE401 usb webcam driver D: ZD1201 usb wireless lan driver + D: INP3 support for the NET/ROM stack S: Maastrichterweg 63 S: 5554 GG Valkenswaard S: The Netherlands diff -rcN linux-source-2.6.32_clean/include/linux/netrom.h linux-source-2.6.32/include/linux/netrom.h *** linux-source-2.6.32_clean/include/linux/netrom.h 2009-12-03 14:51:21.000000000 +1100 --- linux-source-2.6.32/include/linux/netrom.h 2012-12-14 17:00:59.000000000 +1100 *************** *** 16,21 **** --- 16,24 ---- #define NETROM_IDLE 7 #define SIOCNRDECOBS (SIOCPROTOPRIVATE+2) + /* 3 and 4 have been used in the past, so we don't use them for a while... */ + #define SIOCNRSETMNEM (SIOCPROTOPRIVATE+5) + #define SIOCNRGETMNEM (SIOCPROTOPRIVATE+6) struct nr_route_struct { #define NETROM_NEIGH 0 diff -rcN linux-source-2.6.32_clean/include/net/netrom.h linux-source-2.6.32/include/net/netrom.h *** linux-source-2.6.32_clean/include/net/netrom.h 2012-09-23 05:24:40.000000000 +1000 --- linux-source-2.6.32/include/net/netrom.h 2012-12-14 17:00:59.000000000 +1100 *************** *** 9,14 **** --- 9,15 ---- #include #include + #include #include #define NR_NETWORK_LEN 15 *************** *** 37,42 **** --- 38,50 ---- NR_STATE_3 }; + /* Define INP3 State constants */ + enum { + NR_INP_STATE_0, /* Not recognized as an INP neighbour */ + NR_INP_STATE_RTT, /* Got RTT back, but no RIPs yet... */ + NR_INP_STATE_INP, /* Recognized as a full INP neighbour */ + }; + #define NR_COND_ACK_PENDING 0x01 #define NR_COND_REJECT 0x02 #define NR_COND_PEER_RX_BUSY 0x04 *************** *** 59,64 **** --- 67,77 ---- #define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */ #define NR_MAX_PACKET_SIZE 236 /* Maximum Packet Length - 236 */ + struct nr_dev_priv { + struct net_device_stats; + char mnemonic[7]; + }; + struct nr_sock { struct sock sock; ax25_address user_addr, source_addr, dest_addr; *************** *** 93,104 **** --- 106,121 ---- unsigned int number; unsigned char failed; atomic_t refcount; + unsigned char inp_state; /* inp3 state */ + unsigned int rtt; /* inp3 return-time */ }; struct nr_route { unsigned char quality; unsigned char obs_count; struct nr_neigh *neighbour; + unsigned int tt; /* inp3 target-time */ + unsigned int hops; /* inp3 hop counter */ }; struct nr_node { *************** *** 110,121 **** --- 127,145 ---- struct nr_route routes[3]; atomic_t refcount; spinlock_t node_lock; + unsigned int ltt; /* inp3 last send target-time */ }; /********************************************************************* * nr_node & nr_neigh lists, refcounting and locking *********************************************************************/ + extern spinlock_t nr_node_list_lock; + extern spinlock_t nr_neigh_list_lock; + + extern struct hlist_head nr_node_list; + extern struct hlist_head nr_neigh_list; + #define nr_node_hold(__nr_node) \ atomic_inc(&((__nr_node)->refcount)) *************** *** 207,212 **** --- 231,242 ---- extern void nr_check_iframes_acked(struct sock *, unsigned short); /* nr_route.c */ + extern struct nr_node *nr_node_get(ax25_address *); + extern struct nr_neigh *nr_neigh_get_dev(ax25_address *, struct net_device *); + extern int nr_add_node(ax25_address *, const char *, ax25_address *, + ax25_digi *, struct net_device *, int, int); + extern void nr_sort_node(struct nr_node *); + extern int nr_del_node_found(struct nr_node *, struct nr_neigh *); extern void nr_rt_device_down(struct net_device *); extern struct net_device *nr_dev_first(void); extern struct net_device *nr_dev_get(ax25_address *); *************** *** 266,269 **** --- 296,317 ---- extern void nr_register_sysctl(void); extern void nr_unregister_sysctl(void); + /* inp3.c */ + extern ax25_address inp3_l3rtt_addr; + extern int inp3_set_mnem(ax25_address *, const char *); + extern int inp3_get_mnem(ax25_address *, char *); + extern void inp3_nodes_neg(struct nr_node **, int, struct nr_neigh *, + struct hlist_head *); + extern void inp3_route_neg(struct nr_neigh *); + extern int inp3_rif_rx(struct sk_buff *, ax25_cb *); + extern int inp3_l3rtt_rx(struct sk_buff *, ax25_cb *); + extern int inp3_l3rtt_tx(struct nr_neigh *); + extern void kinp3d_start(void); + extern void kinp3d_stop(void); + + #define TT_HORIZON 60000 + #define MAX_RIPNEG 15 + #define ttlimit(tt) ((tt) > TT_HORIZON ? TT_HORIZON : (tt)) + #define qual2rtt(qual) ((qual) ? (ttlimit((256-(qual))*20)) : TT_HORIZON) + #endif diff -rcN linux-source-2.6.32_clean/net/ax25/Kconfig linux-source-2.6.32/net/ax25/Kconfig *** linux-source-2.6.32_clean/net/ax25/Kconfig 2009-12-03 14:51:21.000000000 +1100 --- linux-source-2.6.32/net/ax25/Kconfig 2012-12-14 17:00:59.000000000 +1100 *************** *** 94,99 **** --- 94,110 ---- To compile this driver as a module, choose M here: the module will be called netrom. + config NETROM_INP + bool "NET/ROM INP3 support" + depends on NETROM + help + INP3 is an extension to the NET/ROM protocol. + It uses time measurements instead of quality to set up routing. + If you say Y here your Linux box will be able to communicate with + other nodes that use this protocol. + If you say N you will get 'old style' NET/ROM behaviour. + If unsure, say N. + config ROSE tristate "Amateur Radio X.25 PLP (Rose)" depends on AX25 diff -rcN linux-source-2.6.32_clean/net/netrom/af_netrom.c linux-source-2.6.32/net/netrom/af_netrom.c *** linux-source-2.6.32_clean/net/netrom/af_netrom.c 2009-12-03 14:51:21.000000000 +1100 --- linux-source-2.6.32/net/netrom/af_netrom.c 2012-12-14 17:00:59.000000000 +1100 *************** *** 7,12 **** --- 7,13 ---- * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) + * Copyright Jeroen Vreeken PE1RXQ (pe1rxq@amsat.org) */ #include #include *************** *** 1254,1260 **** case SIOCNRDECOBS: if (!capable(CAP_NET_ADMIN)) return -EPERM; return nr_rt_ioctl(cmd, argp); ! default: return -ENOIOCTLCMD; } --- 1255,1281 ---- case SIOCNRDECOBS: if (!capable(CAP_NET_ADMIN)) return -EPERM; return nr_rt_ioctl(cmd, argp); ! #ifdef CONFIG_NETROM_INP ! case SIOCNRSETMNEM: { ! struct nr_route_struct nr_route; ! release_sock(sk); ! if (copy_from_user(&nr_route, (void *)arg, sizeof(struct nr_route_struct))) ! return -EFAULT; ! return inp3_set_mnem(&nr_route.callsign, nr_route.mnemonic); ! } ! case SIOCNRGETMNEM: { ! struct nr_route_struct nr_route; ! int ret; ! release_sock(sk); ! if (copy_from_user(&nr_route, (void *)arg, sizeof(struct nr_route_struct))) ! return -EFAULT; ! ret = inp3_get_mnem(&nr_route.callsign, nr_route.mnemonic); ! if (ret >= 0) ! if (copy_to_user((void *)arg, &nr_route, sizeof(struct nr_route_struct))) ! return -EFAULT; ! return ret; ! } ! #endif /* CONFIG_NETROM_INP */ default: return -ENOIOCTLCMD; } *************** *** 1438,1444 **** struct net_device *dev; sprintf(name, "nr%d", i); ! dev = alloc_netdev(0, name, nr_setup); if (!dev) { printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n"); goto fail; --- 1459,1465 ---- struct net_device *dev; sprintf(name, "nr%d", i); ! dev = alloc_netdev(sizeof(struct nr_dev_priv), name, nr_setup); if (!dev) { printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n"); goto fail; *************** *** 1473,1478 **** --- 1494,1504 ---- proc_net_fops_create(&init_net, "nr", S_IRUGO, &nr_info_fops); proc_net_fops_create(&init_net, "nr_neigh", S_IRUGO, &nr_neigh_fops); proc_net_fops_create(&init_net, "nr_nodes", S_IRUGO, &nr_nodes_fops); + + #ifdef CONFIG_NETROM_INP + kinp3d_start(); + #endif + out: return rc; fail: *************** *** 1500,1505 **** --- 1526,1535 ---- { int i; + #ifdef CONFIG_NETROM_INP + kinp3d_stop(); + #endif + proc_net_remove(&init_net, "nr"); proc_net_remove(&init_net, "nr_neigh"); proc_net_remove(&init_net, "nr_nodes"); diff -rcN linux-source-2.6.32_clean/net/netrom/inp3.c linux-source-2.6.32/net/netrom/inp3.c *** linux-source-2.6.32_clean/net/netrom/inp3.c 1970-01-01 10:00:00.000000000 +1000 --- linux-source-2.6.32/net/netrom/inp3.c 2012-12-14 17:01:25.000000000 +1100 *************** *** 0 **** --- 1,848 ---- + /* + * INP3 007 + * + * Copyright 2003, 2004 , Jeroen Vreeken (pe1rxq@amsat.org) + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * I do NOT grant permission to export any of these functions to + * non free modules, so don't bother to ask.... + */ + + #ifdef CONFIG_NETROM_INP + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define INPVERSION "007" + + #define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg) + + #define INP3D_INTERVAL HZ*60 + #define L3RTT_INTERVAL 5 + #define RIF_INTERVAL 24*60 + #define L3RTT_MTU 200 + + #define MAX_RIP 10 + + /* AX.25 address used for l3rtt packets */ + ax25_address inp3_l3rtt_addr={{'L'<<1, '3'<<1, 'R'<<1, 'T'<<1, 'T'<<1, 0x40, 0}}; + + static int kinp3d_running=0; + static struct task_struct *kinp3d_task; + static int kinp3d_rifcount=0; + + #define hopsmin(hops) ((hops)<1 ? 1 : (hops)) + #define qual2hops(qual) ((256-(qual))/8+1) + + inline int rtt2qual(int rtt, int hops) + { + int qual; + + if (rtt>=TT_HORIZON) + return 0; + if (hops>=255) + return 0; + + qual=254-(rtt/20); + if (qual > 256-hops) + qual=254-hops; + if (qual < 1) + qual=0; + return qual; + } + + inline int infotype(int ltt, int tt) + { + if (tt>=TT_HORIZON) { + if (ltt!=TT_HORIZON) + return -1; + else + return 0; + } + if (tt > ltt) + return -1; + + if ((tt*5 < ltt*4) && (ltt-tt > 10)) + return 1; + + return 0; + } + + int inp3_set_mnem(ax25_address *call, const char *mnemonic) + { + struct net_device *dev; + struct nr_dev_priv *priv; + + if ((dev = nr_dev_get(call)) == NULL) + return -EINVAL; + + write_lock(&dev_base_lock); + priv = (struct nr_dev_priv *)netdev_priv(dev); + memcpy(priv->mnemonic, mnemonic, 6); + write_lock(&dev_base_lock); + + dev_put(dev); + + return 0; + } + + int inp3_get_mnem(ax25_address *call, char *mnemonic) + { + struct net_device *dev; + struct nr_dev_priv *priv; + + if ((dev = nr_dev_get(call)) == NULL) + return -EINVAL; + + read_lock(&dev_base_lock); + priv = (struct nr_dev_priv *)netdev_priv(dev); + memcpy(mnemonic, priv->mnemonic, 7); + read_unlock(&dev_base_lock); + dev_put(dev); + + return 0; + } + + static char *inp3_first_mnemonic(void) + { + static char mnemonic[7]; + struct net_device *dev; + struct nr_dev_priv *priv; + + if ((dev = nr_dev_first()) == NULL) + return NULL; + priv = (struct nr_dev_priv *)netdev_priv(dev); + if (priv->mnemonic[0]) + memcpy(mnemonic, priv->mnemonic, 7); + else + memcpy(mnemonic, "#none", 6); + dev_put(dev); + + return mnemonic; + } + + inline static struct sk_buff *new_rif_skb(struct nr_neigh *nr_neigh) + { + struct sk_buff *skb; + int axlen=nr_neigh->dev->hard_header_len; + + if ((skb=alloc_skb(axlen + 222, GFP_ATOMIC)) != NULL) { + skb_reserve(skb, axlen-1); + skb->transport_header=skb->data; + *skb_put(skb, 1)=AX25_P_NETROM; + *skb_put(skb, 1)=0xff; /* RIF signature */ + return skb; + } + return NULL; + } + + inline static void rif_tx(struct nr_neigh *nr_neigh, struct sk_buff *skb) + { + struct net_device *dev; + ax25_cb *ax25s; + + /* only to INP3 nodes */ + if (nr_neigh->inp_state==NR_INP_STATE_0) { + kfree_skb(skb); + return; + } + + if ((dev=nr_dev_first())==NULL) { + kfree_skb(skb); + return; + } + + ax25s=ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, + &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + if (ax25s && nr_neigh->ax25) { + ax25_cb_put(nr_neigh->ax25); + } + if (ax25s) + nr_neigh->ax25=ax25s; + else + kfree_skb(skb); + dev_put(dev); + } + + inline void inp3_ltt_update(int all, int neg) + { + struct nr_node *nr_node; + struct hlist_node *node; + int tt; + + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each(nr_node, node, &nr_node_list) { + nr_node_lock(nr_node); + if (nr_node->routes[0].neighbour->inp_state==NR_INP_STATE_INP) + tt=nr_node->routes[0].tt+nr_node->routes[0].neighbour->rtt; + else + tt=qual2rtt(nr_node->routes[0].quality); + if ((ttlimit(tt)>nr_node->ltt) || + (!neg && infotype(nr_node->ltt, tt)) || + all) + nr_node->ltt=ttlimit(tt); + nr_node_unlock(nr_node); + } + spin_unlock_bh(&nr_node_list_lock); + } + + static int inp3_rif_tx(struct nr_neigh *nr_neigh, int all) { + struct nr_node *nr_node; + struct hlist_node *node; + struct sk_buff *skb; + unsigned char *rip=NULL; + int ripcount; + int route; + int tt; + int hops; + + skb=new_rif_skb(nr_neigh); + if (!skb) + return -1; + ripcount=0; + + if (all) { + struct net_device *dev; + struct nr_dev_priv *priv; + /* first a few rips about ourself */ + read_lock(&dev_base_lock); + for_each_netdev(&init_net, dev) { + if ((dev->flags & IFF_UP) && dev->type==ARPHRD_NETROM) { + rip=skb_put(skb, 12); + if (rip < 0x100 || dev->dev_addr < 0x100) { + printk(KERN_CRIT "shit: %p %p\n", rip, dev->dev_addr); + } else + memcpy(rip, (ax25_address *)dev->dev_addr, 7); + *(rip+7)=1; /* hops */ + *(rip+8)=0; /* tt */ + *(rip+9)=0; /* tt */ + priv=(struct nr_dev_priv *)netdev_priv(dev); + *(rip+10)=2+strlen(priv->mnemonic); + *(rip+11)=0; + rip=skb_put(skb, strlen(priv->mnemonic)+1); + strcpy(rip, priv->mnemonic); + *(rip+strlen(priv->mnemonic))=0; /* eop */ + ripcount++; + } + } + read_unlock(&dev_base_lock); + } + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each(nr_node, node, &nr_node_list) { + nr_node_lock(nr_node); + route=nr_node->count; + /* use alternative reverse if routes[0] is backward */ + if (nr_node->routes[0].neighbour!=nr_neigh) + route=0; + else if (nr_node->count > 1) + route=1; + else { + nr_node_unlock(nr_node); + continue; + } + if (nr_node->routes[route].neighbour->inp_state==NR_INP_STATE_INP) { + hops=nr_node->routes[route].hops; + tt=nr_node->routes[route].tt+nr_node->routes[route].neighbour->rtt; + } else { + hops=qual2hops(nr_node->routes[route].quality); + tt=qual2rtt(nr_node->routes[route].quality); + } + if (hops+1<256 && (infotype(nr_node->ltt, tt) || all)) { + rip=skb_put(skb, 12); + if (rip < 0x100 || &nr_node->callsign < 0x100) { + printk(KERN_CRIT "shit2: %p %p\n", rip, &nr_node->callsign); + } else + memcpy(rip, &nr_node->callsign, 7); + *(rip+7)=hopsmin(hops)+1; + *(rip+8)=ttlimit(tt) / 256; + *(rip+9)=ttlimit(tt) & 0xff; + *(rip+10)=strlen(nr_node->mnemonic)+2; + *(rip+11)=0x00; + rip=skb_put(skb, strlen(nr_node->mnemonic)+1); + strcpy(rip, nr_node->mnemonic); + *(rip+strlen(nr_node->mnemonic))=0; + ripcount++; + } + if (ripcount>=MAX_RIP) { + rif_tx(nr_neigh, skb); + skb=new_rif_skb(nr_neigh); + if (!skb) { + nr_node_unlock(nr_node); + spin_unlock_bh(&nr_node_list_lock); + return -1; + } + ripcount=0; + } + nr_node_unlock(nr_node); + } + spin_unlock_bh(&nr_node_list_lock); + if (ripcount) + rif_tx(nr_neigh, skb); + else + kfree_skb(skb); + return 0; + } + + /* + Sends negative info about neg_nodes to neigh_list. + Expects parsing of neigh_list to be safe. + */ + void inp3_nodes_neg(struct nr_node *neg_node[], int neg_nodes, + struct nr_neigh *origin_neigh, struct hlist_head *neigh_list) + { + int i; + int route; + int tt; + int hops; + struct nr_neigh *nr_neigh; + struct hlist_node *node; + + nr_neigh_for_each(nr_neigh, node, neigh_list) { + int ripcount=0; + unsigned char *rip; + struct sk_buff *skb; + + skb=new_rif_skb(nr_neigh); + if (!skb) { + return; + } + for (i=0; iltt>=TT_HORIZON) { + nr_node_unlock(neg_node[i]); + continue; + } + rip=skb_put(skb, 7); + memcpy(rip, &neg_node[i]->callsign, 7); + route=neg_node[i]->count; + if (neg_node[i]->routes[0].neighbour->rtt+neg_node[i]->routes[0].ttroutes[0].neighbour!=nr_neigh) { + route=0; + } else if (neg_node[i]->count > 1 && neg_node[i]->routes[1].neighbour!=nr_neigh) { + route=1; + } else if (neg_node[i]->count > 2) { + route=2; + } + if (route!=neg_node[i]->count) { + if (neg_node[i]->routes[route].neighbour->inp_state==NR_INP_STATE_INP) { + hops=neg_node[i]->routes[route].hops; + tt=neg_node[i]->routes[route].tt+neg_node[i]->routes[route].neighbour->rtt; + } else { + hops=qual2hops(neg_node[i]->routes[route].quality); + tt=qual2rtt(neg_node[i]->routes[route].quality); + } + } else { + hops=254; + tt=TT_HORIZON; + } + if ((tt>=TT_HORIZON && origin_neigh==nr_neigh) || + tt<=neg_node[i]->ltt) { + nr_node_unlock(neg_node[i]); + continue; + } + rip=skb_put(skb, 4); + *(rip+0)=hopsmin(hops)+1; + *(rip+1)=ttlimit(tt) / 256; + *(rip+2)=ttlimit(tt) & 0xff; + *(rip+3)=0; + ripcount++; + nr_node_unlock(neg_node[i]); + } + if (ripcount) + rif_tx(nr_neigh, skb); + else + kfree_skb(skb); + } + for (i=0; icount-1; route>=0; route--) { + if (neg_node[i]->routes[route].neighbour->rtt+neg_node[i]->routes[route].tt>=TT_HORIZON) { + nr_node_hold(neg_node[i]); + nr_del_node_found(neg_node[i], neg_node[i]->routes[route].neighbour); + route=-1; + } + } + nr_node_put(neg_node[i]); + } + inp3_ltt_update(0, 1); + } + + void inp3_route_neg(struct nr_neigh *nr_neigh) + { + struct nr_node *nr_node; + struct nr_node **neg_node; + struct hlist_node *node, *nodet; + int neg_nodes=0; + + neg_node=kmalloc(sizeof(struct nr_node *)*MAX_RIPNEG, GFP_ATOMIC); + if (!neg_node) { + nr_neigh_put(nr_neigh); + return; + } + spin_lock_bh(&nr_neigh_list_lock); + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each_safe(nr_node, node, nodet, &nr_node_list) { + nr_node_lock(nr_node); + if (nr_node->routes[0].neighbour==nr_neigh) { + if (neg_nodes>=MAX_RIPNEG) { + inp3_nodes_neg(neg_node, neg_nodes, nr_neigh, &nr_neigh_list); + neg_nodes=0; + } + nr_node_hold(nr_node); + neg_node[neg_nodes]=nr_node; + neg_nodes++; + } + nr_node_unlock(nr_node); + } + if (neg_nodes) { + inp3_nodes_neg(neg_node, neg_nodes, nr_neigh, &nr_neigh_list); + neg_nodes=0; + } + kfree(neg_node); + spin_unlock_bh(&nr_node_list_lock); + spin_unlock_bh(&nr_neigh_list_lock); + return; + } + + int inp3_rif_rx(struct sk_buff *skb, ax25_cb *ax25) + { + struct nr_node **neg_node; + int neg_nodes=0; + ax25_address *nodecall=NULL; + struct nr_neigh *nr_neigh=NULL; + struct nr_node *nr_node=NULL; + int hops=0; + int tt=0; + int qual=0; + unsigned char *dptr; + unsigned char mnemonic[7]; + int i; + int optlen, opttype, oldroute, oldtt; + + nr_neigh=nr_neigh_get_dev(&ax25->dest_addr, ax25->ax25_dev->dev); + if (!nr_neigh) + return 1; + neg_node=kmalloc(sizeof(struct nr_node *)*MAX_RIPNEG, GFP_ATOMIC); + if (!neg_node) { + nr_neigh_put(nr_neigh); + return 1; + } + dptr=skb->data+1; + /* continue until end of packet */ + while (dptrdata+skb->len-10) { + mnemonic[0]=0; + nodecall=(ax25_address *)dptr; + dptr+=7; + hops=*dptr++; + tt=(dptr[0]<<8)+dptr[1]; + dptr+=2; + while (*dptr && dptr+*dptrdata+skb->len) { + optlen=*dptr; + opttype=*(dptr+1); + if (opttype==0x00) { + if (optlen-2>6) + optlen=8; + memcpy(mnemonic, dptr+2, optlen-2); + mnemonic[optlen-2]=0; + } + dptr+=*dptr; + } + dptr++; + nr_neigh->inp_state=NR_INP_STATE_INP; + /* Over the horizon? */ + if (tt+nr_neigh->rtt>TT_HORIZON || hops==255) + tt=TT_HORIZON; + qual=rtt2qual(nr_neigh->rtt+tt, hops); + /* Make sure node exists */ + nr_add_node(nodecall, mnemonic, &nr_neigh->callsign, + nr_neigh->digipeat, nr_neigh->dev, + nr_neigh->inp_state?qual:0, + sysctl_netrom_obsolescence_count_initialiser); + nr_node=nr_node_get(nodecall); + if (!nr_node) + break; + nr_node_lock(nr_node); + for (i=0; icount; i++) + if (nr_node->routes[i].neighbour==nr_neigh) + break; + oldroute=i; + oldtt=nr_node->routes[0].neighbour->rtt+nr_node->routes[0].tt; + if (i < nr_node->count) { + nr_node->routes[i].tt=tt; + nr_node->routes[i].hops=hops; + nr_node->routes[i].quality=qual; + } + if (!nr_neigh->inp_state) { + nr_node_unlock(nr_node); + nr_node_put(nr_node); + break; + } + /* Call nr_sort_node in case a better route is now known */ + nr_sort_node(nr_node); + nr_node_unlock(nr_node); + /* Is it negative information? */ + if ((i==0 && infotype(nr_node->ltt, + nr_node->routes[0].tt+nr_node->routes[0].neighbour->rtt)==-1)) { + if (neg_nodes>=MAX_RIPNEG) { + spin_lock_bh(&nr_neigh_list_lock); + inp3_nodes_neg(neg_node, neg_nodes, nr_neigh, &nr_neigh_list); + spin_unlock_bh(&nr_neigh_list_lock); + neg_nodes=0; + } + neg_node[neg_nodes]=nr_node; + neg_nodes++; + break; + } + /* If its positive information we don't propagate it yet. */ + nr_node_put(nr_node); + } + if (neg_nodes) { + spin_lock_bh(&nr_neigh_list_lock); + inp3_nodes_neg(neg_node, neg_nodes, nr_neigh, &nr_neigh_list); + spin_unlock_bh(&nr_neigh_list_lock); + } + kfree(neg_node); + nr_neigh_put(nr_neigh); + return 0; + } + + /* + According to the INP3 spec we are free to put pretty much anything in + the l3rtt measurement frames.... + However atleast XNET seams to be picky on what it accepts. + Both XNET and TNN currently use a format they describe as LEVEL3_V2.1 + however there doesn't seem to be any format description of this format. + Here we try to emulate that format as close as possible... + */ + + int inp3_l3rtt_tx(struct nr_neigh *nr_neigh) + { + struct ax25_cb *ax25s; + struct net_device *dev; + struct sk_buff *skb; + struct timeval tv; + unsigned char *rtt_data; + + if ((dev=nr_dev_first()) == NULL) + return 0; + + skb=alloc_skb(nr_neigh->dev->hard_header_len + L3RTT_MTU+1, GFP_ATOMIC); + if (!skb) { + dev_put(dev); + printk(KERN_CRIT "inp3_l3rtt_tx: alloc_skb failed\n"); + return 0; + } + skb_reserve(skb, nr_neigh->dev->hard_header_len); + skb->transport_header=skb->data; + + rtt_data=skb_put(skb, L3RTT_MTU+1); + if (!rtt_data) { + dev_put(dev); + printk(KERN_CRIT "inp3_l3rtt_tx: skb_put failed\n"); + kfree_skb(skb); + return 0; + } + rtt_data[0]=AX25_P_NETROM; + memset(rtt_data+1, 0x20, L3RTT_MTU); + memcpy(rtt_data+1, dev->dev_addr, 7); + memcpy(rtt_data+7+1, &inp3_l3rtt_addr, 7); + rtt_data[14+1]=0x02; /* ttl */ + rtt_data[15+1]=0x00; + rtt_data[16+1]=0x00; + rtt_data[17+1]=0x00; + rtt_data[18+1]=0x00; + rtt_data[19+1]=NR_INFO; + do_gettimeofday(&tv); + /* Has to stay within L3RTT_MTU! */ + rtt_data[20+1+sprintf(rtt_data+20+1, + "L3RTT: %10d %10d %10d %10d %-6s %11s %s $M%d $N", + (int)tv.tv_sec, + nr_neigh->rtt, + nr_neigh->rtt, + (int)tv.tv_usec, + inp3_first_mnemonic(), + "LEVEL3_V2.1", "LINUX" INPVERSION, + TT_HORIZON + )]=0x20; + rtt_data[L3RTT_MTU]=0x0d; + + + ax25s=ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, + &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + if (ax25s && nr_neigh->ax25) { + ax25_cb_put(nr_neigh->ax25); + } + if (!ax25s) { + kfree_skb(skb); + } else { + nr_neigh->ax25=ax25s; + } + dev_put(dev); + return 1; + } + + int inp3_l3rtt_rx(struct sk_buff *skb, ax25_cb *ax25) + { + int neg_nodes=0; + struct nr_node **neg_node; + ax25_address *nr_src; + struct hlist_node *node, *node2; + struct nr_neigh *nr_neigh=NULL; + struct nr_node *nr_node=NULL; + struct net_device *dev; + struct sk_buff *skbret; + unsigned char *dptr; + struct timeval tv, tvret; + char *rttdata; + int rtt; + int qual; + int i; + int oldroute=0, oldtt=0; + + nr_src=(ax25_address *)(skb->data); + + /* Shouldn't happen! */ + if (!ax25) { + info("Should not get my own l3rtt frames!!!"); + return 0; + } + + if ((dev=nr_dev_first()) == NULL) + return 0; + + /* Is it a reply to one of our l3rtt frames? */ + if (!ax25cmp(nr_src, (ax25_address *)dev->dev_addr)) { + if (skb->len < 29) { + dev_put(dev); + return 0; + } + rttdata=skb->data; + /* Add terminating null */ + rttdata[skb->len-1]=0; + dptr=rttdata+7+20; + while(*dptr==0x20) dptr++; + tvret.tv_sec=simple_strtoul(dptr, (char **)(&dptr), 0); + while(*dptr==0x20) dptr++; + i=simple_strtoul(dptr, (char **)(&dptr), 0); + while(*dptr==0x20) dptr++; + i=simple_strtoul(dptr, (char **)(&dptr), 0); + while(*dptr==0x20) dptr++; + tvret.tv_usec=simple_strtoul(dptr, (char **)(&dptr), 0); + do_gettimeofday(&tv); + rtt=((tv.tv_sec-tvret.tv_sec)*1000+(tv.tv_usec+10000)/1000)/20; + if (!rtt) + rtt=1; + nr_node=nr_node_get(&ax25->dest_addr); + if (!nr_node) { + dev_put(dev); + return 0; + } + nr_neigh=nr_neigh_get_dev(&ax25->dest_addr, ax25->ax25_dev->dev); + if (!nr_neigh) { + nr_node_put(nr_node); + dev_put(dev); + } + nr_node_lock(nr_node); + for (i=0; icount; i++) + if (nr_node->routes[i].neighbour==nr_neigh) + nr_node->routes[i].hops=0; + nr_node_unlock(nr_node); + nr_node_put(nr_node); + /* New link? Give it a higher rtt */ + if (nr_neigh->inp_state==NR_INP_STATE_0) { + nr_neigh->rtt=rtt+10; + nr_neigh->inp_state=NR_INP_STATE_RTT; + inp3_rif_tx(nr_neigh, 1); + } + /* Smooth rtt */ + rtt=nr_neigh->rtt=(nr_neigh->rtt+rtt)/2; + if (rtt>=TT_HORIZON) { + inp3_route_neg(nr_neigh); + nr_neigh_put(nr_neigh); + dev_put(dev); + return 0; + } + + /* set all routes of this neighbour with new rtt */ + neg_node=kmalloc(sizeof(struct nr_node *)*MAX_RIPNEG, GFP_ATOMIC); + if (!neg_node) { + nr_neigh_put(nr_neigh); + dev_put(dev); + return 0; + } + spin_lock_bh(&nr_neigh_list_lock); + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each_safe(nr_node, node, node2, &nr_node_list) { + for (i=0; icount; i++) { + if (nr_node->routes[i].neighbour == nr_neigh) { + nr_node_lock(nr_node); + qual=rtt2qual(nr_neigh->rtt+nr_node->routes[i].tt, + nr_node->routes[i].hops); + nr_node->routes[i].quality=qual; + oldroute=i; + oldtt=nr_node->routes[0].neighbour->rtt+ + nr_node->routes[0].tt; + nr_sort_node(nr_node); + nr_node_unlock(nr_node); + } + } + if (infotype(nr_node->ltt, + nr_node->routes[0].tt+nr_node->routes[0].neighbour->rtt)==-1) { + if (neg_nodes>=MAX_RIPNEG) { + inp3_nodes_neg(neg_node, neg_nodes, nr_neigh, &nr_neigh_list); + neg_nodes=0; + } + nr_node_hold(nr_node); + neg_node[neg_nodes]=nr_node; + neg_nodes++; + } + } + spin_unlock_bh(&nr_node_list_lock); + if (neg_nodes) { + inp3_nodes_neg(neg_node, neg_nodes, nr_neigh, &nr_neigh_list); + } + spin_unlock_bh(&nr_neigh_list_lock); + kfree(neg_node); + nr_neigh_put(nr_neigh); + } else { + struct ax25_cb *ax25s; + + nr_neigh=nr_neigh_get_dev(nr_src, ax25->ax25_dev->dev); + if (!nr_neigh) { + dev_put(dev); + return 0; + } + if ((skbret=skb_copy(skb, GFP_ATOMIC))==NULL) { + dev_put(dev); + nr_neigh_put(nr_neigh); + return 0; + } + skbret->data[14]--; + dptr=skb_push(skbret, 1); + *dptr=AX25_P_NETROM; + ax25s=ax25_send_frame(skbret, 256, (ax25_address *)dev->dev_addr, + &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + if (ax25s && nr_neigh->ax25) { + ax25_cb_put(nr_neigh->ax25); + } + if (!ax25s) { + kfree_skb(skbret); + } else { + nr_neigh->ax25=ax25s; + } + nr_neigh_put(nr_neigh); + } + dev_put(dev); + return 0; + } + + static void kinp3d_l3rtt(void) + { + struct nr_neigh *nr_neigh; + struct hlist_node *node; + + spin_lock_bh(&nr_neigh_list_lock); + nr_neigh_for_each(nr_neigh, node, &nr_neigh_list) { + inp3_l3rtt_tx(nr_neigh); + } + spin_unlock_bh(&nr_neigh_list_lock); + } + + static void kinp3d_rif(void) + { + struct nr_neigh *nr_neigh; + struct hlist_node *node; + + kinp3d_rifcount++; + /* Send all changed routes to the nodes, then mark them changed + Once in a while we send the complete list */ + spin_lock_bh(&nr_neigh_list_lock); + nr_neigh_for_each(nr_neigh, node, &nr_neigh_list) { + if (nr_neigh->inp_state==NR_INP_STATE_INP) { + if (kinp3d_rifcount>=RIF_INTERVAL) + inp3_rif_tx(nr_neigh, 1); + else + inp3_rif_tx(nr_neigh, 0); + } + } + spin_unlock_bh(&nr_neigh_list_lock); + if (kinp3d_rifcount>=RIF_INTERVAL) { + kinp3d_rifcount=0; + inp3_ltt_update(1, 0); + } else + inp3_ltt_update(0, 0); + } + + static int kinp3d_thread(void *ptr) + { + int l3rttcnt=0; + + kinp3d_running=1; + + printk(KERN_INFO "PE1RXQ INP3 for Linux. Version " INPVERSION "\n"); + do { + if (++l3rttcnt>L3RTT_INTERVAL) { + kinp3d_l3rtt(); + l3rttcnt=0; + } else { + /* Prevent rifs from messing up the rtt value */ + kinp3d_rif(); + } + current->state=TASK_INTERRUPTIBLE; + schedule_timeout(INP3D_INTERVAL); + } while (!kthread_should_stop()); + + info("kinp3 exiting"); + kinp3d_running=0; + + return 1; + } + + void kinp3d_start(void) + { + struct task_struct *t; + int ret; + + t=kthread_run(kinp3d_thread, NULL, "kinp3d"); + if (IS_ERR(t)) { + info("failed to start kinp3d"); + ret = PTR_ERR(t); + return ret; + } + kinp3d_task=t; + return; + } + + void kinp3d_stop(void) + { + int ret; + + ret=kthread_stop(kinp3d_task); + if (!ret) { + /* Wait 10 seconds */ + int count = 10 * HZ; + + while (kinp3d_running && --count) { + current->state=TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if (!count) + info("failed to kill kinp3d"); + } + } + + + #endif /* CONFIG_NETROM_INP */ diff -rcN linux-source-2.6.32_clean/net/netrom/Makefile linux-source-2.6.32/net/netrom/Makefile *** linux-source-2.6.32_clean/net/netrom/Makefile 2009-12-03 14:51:21.000000000 +1100 --- linux-source-2.6.32/net/netrom/Makefile 2012-12-14 17:00:59.000000000 +1100 *************** *** 7,9 **** --- 7,10 ---- netrom-y := af_netrom.o nr_dev.o nr_in.o nr_loopback.o \ nr_out.o nr_route.o nr_subr.o nr_timer.o netrom-$(CONFIG_SYSCTL) += sysctl_net_netrom.o + netrom-$(CONFIG_NETROM_INP) += inp3.o diff -rcN linux-source-2.6.32_clean/net/netrom/nr_route.c linux-source-2.6.32/net/netrom/nr_route.c *** linux-source-2.6.32_clean/net/netrom/nr_route.c 2012-09-23 05:24:40.000000000 +1000 --- linux-source-2.6.32/net/netrom/nr_route.c 2012-12-14 17:00:59.000000000 +1100 *************** *** 7,12 **** --- 7,13 ---- * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) + * Copyright Jeroen Vreeken PE1RXQ (pe1rxq@amsat.org) */ #include #include *************** *** 39,50 **** static unsigned int nr_neigh_no = 1; ! static HLIST_HEAD(nr_node_list); ! static DEFINE_SPINLOCK(nr_node_list_lock); ! static HLIST_HEAD(nr_neigh_list); ! static DEFINE_SPINLOCK(nr_neigh_list_lock); ! static struct nr_node *nr_node_get(ax25_address *callsign) { struct nr_node *found = NULL; struct nr_node *nr_node; --- 40,57 ---- static unsigned int nr_neigh_no = 1; ! /* ! Unfortunatly at some times both the nr_neigh_list and nr_node_list have ! to be locked at the same time. ! In this case the following rule applies: ! First lock the nr_neigh_list, second the nr_node_list. ! */ ! HLIST_HEAD(nr_node_list); ! spinlock_t nr_node_list_lock = SPIN_LOCK_UNLOCKED; ! HLIST_HEAD(nr_neigh_list); ! spinlock_t nr_neigh_list_lock = SPIN_LOCK_UNLOCKED; ! struct nr_node *nr_node_get(ax25_address *callsign) { struct nr_node *found = NULL; struct nr_node *nr_node; *************** *** 61,67 **** return found; } ! static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign, struct net_device *dev) { struct nr_neigh *found = NULL; --- 68,74 ---- return found; } ! struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign, struct net_device *dev) { struct nr_neigh *found = NULL; *************** *** 86,98 **** * Add a new route to a node, and in the process add the node and the * neighbour if it is new. */ ! static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev, int quality, int obs_count) { struct nr_node *nr_node; struct nr_neigh *nr_neigh; - struct nr_route nr_route; int i, found; struct net_device *odev; --- 93,104 ---- * Add a new route to a node, and in the process add the node and the * neighbour if it is new. */ ! int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev, int quality, int obs_count) { struct nr_node *nr_node; struct nr_neigh *nr_neigh; int i, found; struct net_device *odev; *************** *** 125,130 **** --- 131,140 ---- nr_node_unlock(nr_nodet); } spin_unlock_bh(&nr_node_list_lock); + #ifdef CONFIG_NETROM_INP + /* Send an l3rtt frame */ + inp3_l3rtt_tx(nr_neigh); + #endif } if (nr_neigh != NULL) *************** *** 152,157 **** --- 162,171 ---- nr_neigh->count = 0; nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; + #ifdef CONFIG_NETROM_INP + nr_neigh->inp_state= NR_INP_STATE_0; + nr_neigh->rtt = 0; + #endif atomic_set(&nr_neigh->refcount, 1); if (ax25_digi != NULL && ax25_digi->ndigi > 0) { *************** *** 170,175 **** --- 184,193 ---- hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); nr_neigh_hold(nr_neigh); spin_unlock_bh(&nr_neigh_list_lock); + #ifdef CONFIG_NETROM_INP + /* Send an l3rtt frame to the new neighbour */ + inp3_l3rtt_tx(nr_neigh); + #endif } if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) *************** *** 211,217 **** --- 229,244 ---- for (found = 0, i = 0; i < nr_node->count; i++) { if (nr_node->routes[i].neighbour == nr_neigh) { + #ifdef CONFIG_NETROM_INP + /* Only set quality for non-inp3 nodes */ + if (!nr_node->routes[i].tt && + !(!ax25cmp(nr, ax25) && nr_neigh->inp_state==NR_INP_STATE_INP)) + nr_node->routes[i].quality = quality; + else + quality = nr_node->routes[i].quality; + #else nr_node->routes[i].quality = quality; + #endif nr_node->routes[i].obs_count = obs_count; found = 1; break; *************** *** 251,260 **** } } ! /* Now re-sort the routes in quality order */ switch (nr_node->count) { case 3: ! if (nr_node->routes[1].quality > nr_node->routes[0].quality) { switch (nr_node->which) { case 0: nr_node->which = 1; break; case 1: nr_node->which = 0; break; --- 278,334 ---- } } ! nr_neigh_put(nr_neigh); ! ! nr_sort_node(nr_node); ! ! nr_node_unlock(nr_node); ! nr_node_put(nr_node); ! return 0; ! } ! ! /* ! Calculate route time (less is better) ! Also take hop count in account. ! */ ! static inline int route2time(struct nr_node *nr_node, struct nr_route *route) ! { ! int time; ! ! if (!route->tt && ! !(!ax25cmp(&nr_node->callsign, &route->neighbour->callsign) && ! route->neighbour->inp_state==NR_INP_STATE_INP) ) { ! /* Netrom routes get a hopscount of 255 to give a slight ! preference to inp3 routes */ ! time=(qual2rtt(route->quality)<<8) +255; ! } else { ! time=(route->tt+route->neighbour->rtt)<<8; ! time|=route->hops; ! } ! return time; ! } ! ! /* ! This function was split of of nr_add_node because the inp3 code ! needed only the sorting functionality. ! Traditionally sorting was done in quality order. ! A problem arose when the precission of the l3rtt measurements ! became better then that of its quality. Because of this all ! sorting is now done based on time and hopcount. ! ! Jeroen ! ! This code expects the node to be locked. ! */ ! void nr_sort_node(struct nr_node *nr_node) ! { ! struct nr_route nr_route; ! int i; ! switch (nr_node->count) { case 3: ! if (route2time(nr_node, &nr_node->routes[1]) < ! route2time(nr_node, &nr_node->routes[0])) { switch (nr_node->which) { case 0: nr_node->which = 1; break; case 1: nr_node->which = 0; break; *************** *** 264,294 **** nr_node->routes[0] = nr_node->routes[1]; nr_node->routes[1] = nr_route; } ! if (nr_node->routes[2].quality > nr_node->routes[1].quality) { switch (nr_node->which) { ! case 1: nr_node->which = 2; ! break; ! ! case 2: nr_node->which = 1; ! break; ! ! default: ! break; } nr_route = nr_node->routes[1]; nr_node->routes[1] = nr_node->routes[2]; nr_node->routes[2] = nr_route; } case 2: ! if (nr_node->routes[1].quality > nr_node->routes[0].quality) { switch (nr_node->which) { ! case 0: nr_node->which = 1; ! break; ! ! case 1: nr_node->which = 0; ! break; ! ! default: break; } nr_route = nr_node->routes[0]; nr_node->routes[0] = nr_node->routes[1]; --- 338,361 ---- nr_node->routes[0] = nr_node->routes[1]; nr_node->routes[1] = nr_route; } ! if (route2time(nr_node, &nr_node->routes[2]) < ! route2time(nr_node, &nr_node->routes[1])) { switch (nr_node->which) { ! case 1: nr_node->which = 2; break; ! case 2: nr_node->which = 1; break; ! default: break; } nr_route = nr_node->routes[1]; nr_node->routes[1] = nr_node->routes[2]; nr_node->routes[2] = nr_route; } case 2: ! if (route2time(nr_node, &nr_node->routes[1]) < ! route2time(nr_node, &nr_node->routes[0])) { switch (nr_node->which) { ! case 0: nr_node->which = 1; break; ! case 1: nr_node->which = 0; break; ! default: break; } nr_route = nr_node->routes[0]; nr_node->routes[0] = nr_node->routes[1]; *************** *** 299,315 **** } for (i = 0; i < nr_node->count; i++) { ! if (nr_node->routes[i].neighbour == nr_neigh) { if (i < nr_node->which) nr_node->which = i; break; } } ! ! nr_neigh_put(nr_neigh); ! nr_node_unlock(nr_node); ! nr_node_put(nr_node); ! return 0; } static inline void __nr_remove_node(struct nr_node *nr_node) --- 366,378 ---- } for (i = 0; i < nr_node->count; i++) { ! if (nr_node->routes[i].neighbour->failed == 0) { if (i < nr_node->which) nr_node->which = i; break; } } ! return; } static inline void __nr_remove_node(struct nr_node *nr_node) *************** *** 348,371 **** * "Delete" a node. Strictly speaking remove a route to a node. The node * is only deleted if no routes are left to it. */ ! static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev) { - struct nr_node *nr_node; - struct nr_neigh *nr_neigh; int i; - nr_node = nr_node_get(callsign); - - if (nr_node == NULL) - return -EINVAL; - - nr_neigh = nr_neigh_get_dev(neighbour, dev); - - if (nr_neigh == NULL) { - nr_node_put(nr_node); - return -EINVAL; - } - nr_node_lock(nr_node); for (i = 0; i < nr_node->count; i++) { if (nr_node->routes[i].neighbour == nr_neigh) { --- 411,420 ---- * "Delete" a node. Strictly speaking remove a route to a node. The node * is only deleted if no routes are left to it. */ ! int nr_del_node_found(struct nr_node *nr_node, struct nr_neigh *nr_neigh) { int i; nr_node_lock(nr_node); for (i = 0; i < nr_node->count; i++) { if (nr_node->routes[i].neighbour == nr_neigh) { *************** *** 403,408 **** --- 452,477 ---- return -EINVAL; } + static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev) + { + struct nr_node *nr_node; + struct nr_neigh *nr_neigh; + + nr_node = nr_node_get(callsign); + + if (nr_node == NULL) + return -EINVAL; + + nr_neigh = nr_neigh_get_dev(neighbour, dev); + + if (nr_neigh == NULL) { + nr_node_put(nr_node); + return -EINVAL; + } + + return nr_del_node_found(nr_node, nr_neigh); + } + /* * Lock a neighbour with a quality. */ *************** *** 431,436 **** --- 500,509 ---- nr_neigh->count = 0; nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; + #ifdef CONFIG_NETROM_INP + nr_neigh->inp_state= NR_INP_STATE_0; + nr_neigh->rtt = 0; + #endif atomic_set(&nr_neigh->refcount, 1); if (ax25_digi != NULL && ax25_digi->ndigi > 0) { *************** *** 472,477 **** --- 545,551 ---- return 0; } + #ifdef CONFIG_NETROM_INP /* * Decrement the obsolescence count by one. If a route is reduced to a * count of zero, remove it. Also remove any unlocked neighbours with *************** *** 482,489 **** --- 556,570 ---- struct nr_neigh *nr_neigh; struct nr_node *s; struct hlist_node *node, *nodet; + struct nr_node **neg_node; + int neg_nodes=0; int i; + neg_node=kmalloc(sizeof(struct nr_node *)*MAX_RIPNEG, GFP_ATOMIC); + if (!neg_node) { + return 0; + } + spin_lock_bh(&nr_neigh_list_lock); spin_lock_bh(&nr_node_list_lock); nr_node_for_each_safe(s, node, nodet, &nr_node_list) { nr_node_lock(s); *************** *** 494,499 **** --- 575,597 ---- case 1: /* From 1 -> 0 */ nr_neigh = s->routes[i].neighbour; + nr_neigh_hold(nr_neigh); + + /* This is negative INP info */ + if (i==0) { + /* send negative info about the node */ + s->routes[i].tt=TT_HORIZON; + s->routes[i].hops=254; + nr_node_unlock(s); + if (neg_nodes>=MAX_RIPNEG) { + inp3_nodes_neg(neg_node, neg_nodes, NULL, &nr_neigh_list); + neg_nodes=0; + } + nr_node_hold(s); + neg_node[neg_nodes]=s; + neg_nodes++; + continue; + } nr_neigh->count--; nr_neigh_put(nr_neigh); *************** *** 514,525 **** break; default: s->routes[i].obs_count--; break; - } } if (s->count <= 0) nr_remove_node_locked(s); nr_node_unlock(s); --- 612,682 ---- break; default: + /* we don't use the obs count for INP + neighbours */ + if (s->routes[i].neighbour->inp_state==NR_INP_STATE_INP) + continue; s->routes[i].obs_count--; break; } } + nr_node_unlock(s); + } + spin_unlock_bh(&nr_node_list_lock); + if (neg_nodes) + inp3_nodes_neg(neg_node, neg_nodes, NULL, &nr_neigh_list); + kfree(neg_node); + spin_unlock_bh(&nr_neigh_list_lock); + + return 0; + } + #else + /* + * Decrement the obsolescence count by one. If a route is reduced to a + * count of zero, remove it. Also remove any unlocked neighbours with + * zero nodes routing via it. + */ + static int nr_dec_obs(void) + { + struct nr_neigh *nr_neigh; + struct nr_node *s; + struct hlist_node *node, *nodet; + int i; + + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each_safe(s, node, nodet, &nr_node_list) { + nr_node_lock(s); + for (i = 0; i < s->count; i++) { + switch (s->routes[i].obs_count) { + case 0: /* A locked entry */ + break; + + case 1: /* From 1 -> 0 */ + nr_neigh = s->routes[i].neighbour; + + nr_neigh->count--; + nr_neigh_put(nr_neigh); + + if (nr_neigh->count == 0 && !nr_neigh->locked) + nr_remove_neigh(nr_neigh); + + s->count--; + + switch (i) { + case 0: + s->routes[0] = s->routes[1]; + case 1: + s->routes[1] = s->routes[2]; + case 2: + break; + } + break; + default: + s->routes[i].obs_count--; + break; + } + } if (s->count <= 0) nr_remove_node_locked(s); nr_node_unlock(s); *************** *** 528,533 **** --- 685,691 ---- return 0; } + #endif /* * A device has been removed. Remove its routes and neighbours. *************** *** 730,735 **** --- 888,894 ---- struct nr_neigh *s, *nr_neigh = NULL; struct hlist_node *node; struct nr_node *nr_node = NULL; + int i; spin_lock_bh(&nr_neigh_list_lock); nr_neigh_for_each(s, node, &nr_neigh_list) { *************** *** 744,749 **** --- 903,933 ---- if (nr_neigh == NULL) return; + #ifdef CONFIG_NETROM_INP + /* Set quality to zero for all its routes + by setting the obs_count to 1 we ensure that the node is + removed soon */ + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each(nr_node, node, &nr_node_list) { + nr_node_lock(nr_node); + for (i=0; icount; i++) { + if (nr_node->routes[i].neighbour==nr_neigh) { + nr_node->routes[i].tt=0; + nr_node->routes[i].quality=0; + nr_node->routes[i].obs_count=1; + nr_sort_node(nr_node); + } + } + nr_node_unlock(nr_node); + } + spin_unlock_bh(&nr_node_list_lock); + /* Tell the other neighbours the link has failed */ + nr_neigh->rtt=TT_HORIZON; + inp3_route_neg(nr_neigh); + nr_neigh->inp_state=NR_INP_STATE_0; + nr_neigh->rtt=0; + #endif + nr_neigh->ax25 = NULL; ax25_cb_put(ax25); *************** *** 751,756 **** --- 935,941 ---- nr_neigh_put(nr_neigh); return; } + #ifndef CONFIG_NETROM_INP spin_lock_bh(&nr_node_list_lock); nr_node_for_each(nr_node, node, &nr_node_list) { nr_node_lock(nr_node); *************** *** 760,765 **** --- 945,951 ---- nr_node_unlock(nr_node); } spin_unlock_bh(&nr_node_list_lock); + #endif nr_neigh_put(nr_neigh); } *************** *** 778,783 **** --- 964,976 ---- int ret; struct sk_buff *skbn; + #ifdef CONFIG_NETROM_INP + /* Is it an INP3 Routing Information Frame? */ + if (skb->data[0]==0xff) { + inp3_rif_rx(skb, ax25); + return 0; + } + #endif nr_src = (ax25_address *)(skb->data + 0); nr_dest = (ax25_address *)(skb->data + 7); *************** *** 802,807 **** --- 995,1006 ---- if (!sysctl_netrom_routing_control && ax25 != NULL) return 0; + #ifdef CONFIG_NETROM_INP + /* Is it an INP3 return time measurment frame? */ + if (!ax25cmp(nr_dest, &inp3_l3rtt_addr)) + return inp3_l3rtt_rx(skb, ax25); + #endif + /* Its Time-To-Live has expired */ if (skb->data[14] == 1) { return 0;