summaryrefslogtreecommitdiffstats
path: root/net/netrom
diff options
context:
space:
mode:
Diffstat (limited to 'net/netrom')
-rw-r--r--net/netrom/af_netrom.c130
-rw-r--r--net/netrom/nr_loopback.c6
-rw-r--r--net/netrom/nr_route.c98
3 files changed, 124 insertions, 110 deletions
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index a4b89d294..3ba0b8c81 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -47,6 +47,7 @@
#include <linux/net.h>
#include <linux/stat.h>
#include <net/ax25.h>
+#include <net/ax25_uid.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
@@ -347,6 +348,8 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
+ char devname[IFNAMSIZ];
+ struct net_device *dev;
int opt;
if (level != SOL_NETROM)
@@ -389,6 +392,21 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
sk->protinfo.nr->idle = opt * 60 * HZ;
return 0;
+ case SO_BINDTODEVICE:
+ if (optlen > IFNAMSIZ) optlen = IFNAMSIZ;
+ if (copy_from_user(devname, optval, optlen))
+ return -EFAULT;
+
+ dev = dev_get_by_name(devname);
+ if (dev == NULL) return -ENODEV;
+
+ if (sk->type == SOCK_SEQPACKET &&
+ (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
+ return -EADDRNOTAVAIL;
+
+ sk->protinfo.nr->device=dev;
+ return 0;
+
default:
return -ENOPROTOOPT;
}
@@ -398,15 +416,24 @@ static int nr_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
+ void *valptr;
int val = 0;
- int len;
+ int maxlen, length;
if (level != SOL_NETROM)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
+ if (get_user(maxlen, optlen))
return -EFAULT;
+ if (maxlen < 1)
+ return -EFAULT;
+
+ valptr = (void *) &val;
+ length = min(maxlen, sizeof(int));
+
switch (optname) {
case NETROM_T1:
val = sk->protinfo.nr->t1 / HZ;
@@ -428,16 +455,33 @@ static int nr_getsockopt(struct socket *sock, int level, int optname,
val = sk->protinfo.nr->idle / (60 * HZ);
break;
+ case SO_BINDTODEVICE:
+ dev = sk->protinfo.nr->device;
+
+ if (dev != NULL) {
+ strncpy(devname, dev->name, IFNAMSIZ);
+ length = min(strlen(dev->name)+1, maxlen);
+ devname[length-1] = '\0';
+ } else {
+ *devname = '\0';
+ length = 1;
+ }
+
+ valptr = (void *) devname;
+ break;
+
+
default:
return -ENOPROTOOPT;
}
- len = min(len, sizeof(int));
+ if (put_user(length, optlen))
+ return -EFAULT;
- if (put_user(len, optlen))
+ if (copy_to_user(optval, valptr, length))
return -EFAULT;
- return copy_to_user(optval, &val, len) ? -EFAULT : 0;
+ return 0;
}
static int nr_listen(struct socket *sock, int backlog)
@@ -589,13 +633,12 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
struct net_device *dev;
- ax25_address *user, *source;
+ ax25_address *user = NULL, *source = NULL;
if (sk->zapped == 0)
return -EINVAL;
- if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct
-full_sockaddr_ax25))
+ if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
return -EINVAL;
if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25)))
@@ -604,32 +647,42 @@ full_sockaddr_ax25))
if (addr->fsa_ax25.sax25_family != AF_NETROM)
return -EINVAL;
- if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
- SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n");
- return -EADDRNOTAVAIL;
- }
-
/*
- * Only the super user can set an arbitrary user callsign.
+ * User did not set the interfave with SO_BINDTODEVICE,
+ * use compatibility code.
*/
- if (addr->fsa_ax25.sax25_ndigis == 1) {
- if (!capable(CAP_NET_BIND_SERVICE))
- return -EACCES;
- sk->protinfo.nr->user_addr = addr->fsa_digipeater[0];
- sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call;
- } else {
- source = &addr->fsa_ax25.sax25_call;
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
- return -EPERM;
- user = source;
+ dev = sk->protinfo.nr->device;
+ if (dev == NULL) {
+ if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) {
+ /* device callsign provided */
+ if(ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) &&
+ (dev = nr_dev_get(&addr->fsa_digipeater[0])) == NULL)
+ return -EADDRNOTAVAIL;
+ source = &addr->fsa_digipeater[0];
+ } else {
+ if ((dev = nr_dev_get(&addr->fsax25_call)) == NULL)
+ return -EADDRNOTAVAIL;
+ source = &addr->fsa_ax25.sax25_call;
}
- sk->protinfo.nr->user_addr = *user;
- sk->protinfo.nr->source_addr = *source;
+ sk->protinfo.nr->device = dev;
+ } else {
+ source = (ax25_address *) dev->dev_addr;
}
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ /* FIXME: should not be coupled with AX.25 */
+ user = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name);
+ if (user == NULL && ax25_uid_policy)
+ return -EACCES;
+ }
+
+ if (user == NULL) user = &addr->fsax25_call;
+ sk->protinfo.nr->user_addr = *user;
+ sk->protinfo.nr->source_addr = *source;
+
sk->protinfo.nr->device = dev;
nr_insert_socket(sk);
@@ -643,7 +696,6 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
- ax25_address *user, *source = NULL;
struct net_device *dev;
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
@@ -668,26 +720,8 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
if (addr->sax25_family != AF_NETROM)
return -EINVAL;
- if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
- sk->zapped = 0;
-
- if ((dev = nr_dev_first()) == NULL)
- return -ENETUNREACH;
-
- source = (ax25_address *)dev->dev_addr;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_ADMIN))
- return -EPERM;
- user = source;
- }
-
- sk->protinfo.nr->user_addr = *user;
- sk->protinfo.nr->source_addr = *source;
- sk->protinfo.nr->device = dev;
-
- nr_insert_socket(sk); /* Finish the bind */
- }
+ if (sk->zapped)
+ return -EADDRNOTAVAIL;
sk->protinfo.nr->dest_addr = addr->sax25_call;
diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c
index 5290ae024..519ca55da 100644
--- a/net/netrom/nr_loopback.c
+++ b/net/netrom/nr_loopback.c
@@ -44,10 +44,10 @@ int nr_loopback_queue(struct sk_buff *skb)
{
struct sk_buff *skbn;
- if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) {
- memcpy(skb_put(skbn, skb->len), skb->data, skb->len);
- skbn->h.raw = skbn->data;
+ skbn = skb_clone(skb, GFP_ATOMIC);
+ kfree(skb);
+ if (skbn != NULL) {
skb_queue_tail(&loopback_queue, skbn);
if (!nr_loopback_running())
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index ec0578b51..e6793fb0b 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -60,8 +60,8 @@ static void nr_remove_neigh(struct nr_neigh *);
* Add a new route to a node, and in the process add the node and the
* neighbour if it is new.
*/
-static 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)
+static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_addr_t *ax25_addr,
+ struct net_device *dev, int quality, int obs_count)
{
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
@@ -77,7 +77,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
break;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
+ if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev)
break;
/*
@@ -106,8 +106,8 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- nr_neigh->callsign = *ax25;
- nr_neigh->digipeat = NULL;
+ nr_neigh->addr = *ax25_addr;
+ nr_neigh->addr.src = *((ax25_address *) dev->dev_addr);
nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = sysctl_netrom_default_path_quality;
@@ -116,13 +116,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
nr_neigh->number = nr_neigh_no++;
nr_neigh->failed = 0;
- if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
- if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
- kfree(nr_neigh);
- return -ENOMEM;
- }
- memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
- }
+ /* FIXME: not SMP safe! */
save_flags(flags);
cli();
@@ -133,7 +127,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
restore_flags(flags);
}
- if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
+ if (quality != 0 && ax25cmp(nr, &ax25_addr->dest) == 0 && !nr_neigh->locked)
nr_neigh->quality = quality;
if (nr_node == NULL) {
@@ -294,8 +288,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh)
if ((s = nr_neigh_list) == nr_neigh) {
nr_neigh_list = nr_neigh->next;
restore_flags(flags);
- if (nr_neigh->digipeat != NULL)
- kfree(nr_neigh->digipeat);
kfree(nr_neigh);
return;
}
@@ -304,8 +296,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh)
if (s->next == nr_neigh) {
s->next = nr_neigh->next;
restore_flags(flags);
- if (nr_neigh->digipeat != NULL)
- kfree(nr_neigh->digipeat);
kfree(nr_neigh);
return;
}
@@ -333,7 +323,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
if (nr_node == NULL) return -EINVAL;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
+ if (ax25cmp(neighbour, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev)
break;
if (nr_neigh == NULL) return -EINVAL;
@@ -370,13 +360,13 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
/*
* Lock a neighbour with a quality.
*/
-static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
+static int nr_add_neigh(ax25_addr_t *ax25_addr, struct net_device *dev, unsigned int quality)
{
struct nr_neigh *nr_neigh;
unsigned long flags;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
- if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) {
+ if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) {
nr_neigh->quality = quality;
nr_neigh->locked = 1;
return 0;
@@ -386,8 +376,8 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net
if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- nr_neigh->callsign = *callsign;
- nr_neigh->digipeat = NULL;
+ nr_neigh->addr = *ax25_addr;
+ nr_neigh->addr.src = *((ax25_address *) dev->dev_addr);
nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = quality;
@@ -396,14 +386,6 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net
nr_neigh->number = nr_neigh_no++;
nr_neigh->failed = 0;
- if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
- if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
- kfree(nr_neigh);
- return -ENOMEM;
- }
- memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
- }
-
save_flags(flags);
cli();
@@ -424,7 +406,7 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned
struct nr_neigh *nr_neigh;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
+ if (ax25cmp(callsign, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev)
break;
if (nr_neigh == NULL) return -EINVAL;
@@ -586,31 +568,26 @@ struct net_device *nr_dev_get(ax25_address *addr)
for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
dev_hold(dev);
- goto out;
+ break;
}
}
-out:
+
read_unlock(&dev_base_lock);
return dev;
}
-static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
+static ax25_addr_t *nr_neigh_to_addr(ax25_address *callsign, int ndigis, ax25_address *digipeater)
{
- static ax25_digi ax25_digi;
+ static ax25_addr_t ax25_addr;
int i;
- if (ndigis == 0)
- return NULL;
+ ax25_addr.dest = *callsign;
+ ax25_addr.dcount = ndigis;
- for (i = 0; i < ndigis; i++) {
- ax25_digi.calls[i] = digipeaters[i];
- ax25_digi.repeated[i] = 0;
- }
+ for (i = 0; i < ndigis; i++)
+ ax25_addr.digipeater[i] = digipeater[i];
- ax25_digi.ndigi = ndigis;
- ax25_digi.lastrepeat = -1;
-
- return &ax25_digi;
+ return &ax25_addr;
}
/*
@@ -632,16 +609,13 @@ int nr_rt_ioctl(unsigned int cmd, void *arg)
return -EINVAL;
switch (nr_route.type) {
case NETROM_NODE:
- return nr_add_node(&nr_route.callsign,
- nr_route.mnemonic,
- &nr_route.neighbour,
- nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+ return nr_add_node(&nr_route.callsign, nr_route.mnemonic,
+ nr_neigh_to_addr(&nr_route.neighbour, nr_route.ndigis, nr_route.digipeaters),
dev, nr_route.quality,
nr_route.obs_count);
case NETROM_NEIGH:
- return nr_add_neigh(&nr_route.callsign,
- nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
- dev, nr_route.quality);
+ return nr_add_neigh(nr_neigh_to_addr(&nr_route.callsign, nr_route.ndigis, nr_route.digipeaters),
+ dev, nr_route.quality);
default:
return -EINVAL;
}
@@ -713,8 +687,8 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nr_dest = (ax25_address *)(skb->data + 7);
if (ax25 != NULL)
- nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
- ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser);
+ nr_add_node(nr_src, "", &ax25->addr, ax25->device, 0,
+ sysctl_netrom_obsolescence_count_initialiser);
if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */
if (ax25 == NULL) /* Its from me */
@@ -745,7 +719,13 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
dptr = skb_push(skb, 1);
*dptr = AX25_P_NETROM;
- nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+ /* The next line is dirty hotfix. There is a bug in nr_add_neigh() -
+ * src callsign is taken from dev->dev_addr of device on which the
+ * broadcast was received. But this is AX25 device (and callsign),
+ * not the netrom device, so the callsign is bad.
+ */
+ nr_neigh->addr.src = *((ax25_address*)dev->dev_addr);
+ nr_neigh->ax25 = ax25_send_frame(skb, 256, &nr_neigh->addr, nr_neigh->dev);
return (nr_neigh->ax25 != NULL);
}
@@ -814,16 +794,16 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length)
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d",
nr_neigh->number,
- ax2asc(&nr_neigh->callsign),
+ ax2asc(&nr_neigh->addr.dest),
nr_neigh->dev ? nr_neigh->dev->name : "???",
nr_neigh->quality,
nr_neigh->locked,
nr_neigh->count,
nr_neigh->failed);
- if (nr_neigh->digipeat != NULL) {
- for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
- len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i]));
+ if (nr_neigh->addr.dcount) {
+ for (i = 0; i < nr_neigh->addr.dcount; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->addr.digipeater[i]));
}
len += sprintf(buffer + len, "\n");