summaryrefslogtreecommitdiffstats
path: root/net/rose/af_rose.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/rose/af_rose.c')
-rw-r--r--net/rose/af_rose.c107
1 files changed, 77 insertions, 30 deletions
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index a92bf86f5..07acd3946 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -39,6 +39,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>
@@ -404,6 +405,8 @@ static int rose_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
int opt;
if (level != SOL_ROSE)
@@ -454,6 +457,22 @@ static int rose_setsockopt(struct socket *sock, int level, int optname,
sk->protinfo.rose->qbitincl = opt ? 1 : 0;
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.rose->device = dev;
+ return 0;
+
default:
return -ENOPROTOOPT;
}
@@ -463,14 +482,23 @@ static int rose_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_ROSE)
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 ROSE_DEFER:
@@ -501,16 +529,32 @@ static int rose_getsockopt(struct socket *sock, int level, int optname,
val = sk->protinfo.rose->qbitincl;
break;
+ case SO_BINDTODEVICE:
+ dev = sk->protinfo.rose->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 rose_listen(struct socket *sock, int backlog)
@@ -666,7 +710,8 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sock *sk = sock->sk;
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
struct net_device *dev;
- ax25_address *user, *source;
+ ax25_address *user = NULL;
+ rose_address *source = NULL;
int n;
if (sk->zapped == 0)
@@ -683,21 +728,36 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (addr->srose_ndigis > ROSE_MAX_DIGIS)
return -EINVAL;
+ /*
+ * User did not set interface with SO_BINDTODEVICE
+ * thus we'll use the compatibility code
+ */
+
+ dev = sk->protinfo.rose->device;
+ if (dev == NULL) {
+ if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
+ SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
+ return -EADDRNOTAVAIL;
+ }
- if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
- SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
- return -EADDRNOTAVAIL;
+ source = &addr->srose_addr;
+ sk->protinfo.rose->device = dev;
+ } else {
+ source = (rose_address *) dev->dev_addr;
}
- source = &addr->srose_call;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
+ /* root can do whatever (s)he likes, but anyone else... */
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ /* FIXME: strictly speaking this has nothing to do with AX.25 */
+ user = ax25_find_match_for_uid(current->euid, &addr->srose_call, dev->name);
+ if (user == NULL && ax25_uid_policy)
return -EACCES;
- user = source;
}
- sk->protinfo.rose->source_addr = addr->srose_addr;
+ if (user == NULL) user = &addr->srose_call;
+
+ sk->protinfo.rose->source_addr = *source;
sk->protinfo.rose->source_call = *user;
sk->protinfo.rose->device = dev;
sk->protinfo.rose->source_ndigis = addr->srose_ndigis;
@@ -766,21 +826,8 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0)
return -ENETUNREACH;
- if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
- sk->zapped = 0;
-
- if ((dev = rose_dev_first()) == NULL)
- return -ENETUNREACH;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL)
- return -EINVAL;
-
- memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN);
- sk->protinfo.rose->source_call = *user;
- sk->protinfo.rose->device = dev;
-
- rose_insert_socket(sk); /* Finish the bind */
- }
+ if (sk->zapped)
+ return -EINVAL;
sk->protinfo.rose->dest_addr = addr->srose_addr;
sk->protinfo.rose->dest_call = addr->srose_call;