summaryrefslogtreecommitdiffstats
path: root/net/ipv4/devinet.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/ipv4/devinet.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r--net/ipv4/devinet.c356
1 files changed, 201 insertions, 155 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 0c2d70cae..da0685340 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -34,6 +34,7 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/protocol.h>
@@ -41,16 +42,23 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
+#include <linux/notifier.h>
+#include <linux/net_alias.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+extern struct notifier_block *netdev_chain;
/*
* Determine a default network mask, based on the IP address.
*/
-
-unsigned long ip_get_mask(unsigned long addr)
+
+static unsigned long ip_get_mask(unsigned long addr)
{
unsigned long dst;
- if (addr == 0L)
+ if (ZERONET(addr))
return(0L); /* special case */
dst = ntohl(addr);
@@ -68,184 +76,222 @@ unsigned long ip_get_mask(unsigned long addr)
return(0);
}
-/*
- * Check the address for our address, broadcasts, etc.
- *
- * I intend to fix this to at the very least cache the last
- * resolved entry.
+
+/*
+ * This checks bitmasks for the ioctl calls for devices.
*/
-int ip_chk_addr(unsigned long addr)
+static inline int bad_mask(unsigned long mask, unsigned long addr)
{
+ if (addr & (mask = ~mask))
+ return 1;
+ mask = ntohl(mask);
+ if (mask & (mask+1))
+ return 1;
+ return 0;
+}
+
+
+int devinet_ioctl(unsigned int cmd, void *arg)
+{
+ struct ifreq ifr;
struct device *dev;
-#ifndef CONFIG_IP_CLASSLESS
- unsigned long mask;
+ __u32 addr;
+#ifdef CONFIG_NET_ALIAS
+ int err;
#endif
- /*
- * Accept both `all ones' and `all zeros' as BROADCAST.
- * (Support old BSD in other words). This old BSD
- * support will go very soon as it messes other things
- * up.
- * Also accept `loopback broadcast' as BROADCAST.
+ /*
+ * Fetch the caller's info block into kernel space
*/
- if (addr == INADDR_ANY || addr == INADDR_BROADCAST ||
- addr == htonl(0x7FFFFFFFL))
- return IS_BROADCAST;
-
-#ifndef CONFIG_IP_CLASSLESS
- mask = ip_get_mask(addr);
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
/*
- * Accept all of the `loopback' class A net.
+ * See which interface the caller is talking about.
*/
- if ((addr & mask) == htonl(0x7F000000L))
- return IS_MYADDR;
-#else
- if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
- return IS_MYADDR;
-#endif
-
/*
- * OK, now check the interface addresses. We could
- * speed this by keeping a dev and a dev_up chain.
+ *
+ * net_alias_dev_get(): dev_get() with added alias naming magic.
+ * only allow alias creation/deletion if (getset==SIOCSIFADDR)
+ *
*/
- for (dev = dev_base; dev != NULL; dev = dev->next)
+#ifdef CONFIG_KERNELD
+ dev_load(ifr.ifr_name);
+#endif
+
+#ifdef CONFIG_NET_ALIAS
+ if ((dev = net_alias_dev_get(ifr.ifr_name, cmd == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
+ return(err);
+#else
+ if ((dev = dev_get(ifr.ifr_name)) == NULL)
+ return(-ENODEV);
+#endif
+
+ if (cmd != SIOCSIFADDR && dev->family != AF_INET)
+ return(-EINVAL);
+
+ switch(cmd)
{
- if ((!(dev->flags & IFF_UP)) || dev->family!=AF_INET)
- continue;
- /*
- * If the protocol address of the device is 0 this is special
- * and means we are address hunting (eg bootp).
- */
-
- if (dev->pa_addr == 0)
- return IS_MYADDR;
- /*
- * Is it the exact IP address?
- */
-
- if (addr == dev->pa_addr)
- return IS_MYADDR;
- /*
- * Is it our broadcast address?
- */
-
- if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
- return IS_BROADCAST;
- /*
- * Nope. Check for a subnetwork broadcast.
- */
-
- if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0)
- {
- if ((addr & ~dev->pa_mask) == 0)
- return IS_BROADCAST;
- if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
- return IS_BROADCAST;
- }
+ case SIOCGIFADDR: /* Get interface address (and family) */
+ if (ifr.ifr_addr.sa_family == AF_UNSPEC)
+ {
+ memcpy(ifr.ifr_hwaddr.sa_data, dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family = dev->type;
+ }
+ else
+ {
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_port = 0;
+ }
+ break;
+
+ case SIOCSIFADDR: /* Set interface address (and family) */
-#ifndef CONFIG_IP_CLASSLESS
- /*
- * Nope. Check for Network broadcast.
- */
-
- if (((addr ^ dev->pa_addr) & mask) == 0)
- {
- if ((addr & ~mask) == 0)
- return IS_BROADCAST;
- if ((addr & ~mask) == ~mask)
- return IS_BROADCAST;
- }
-#endif
- }
- if(IN_MULTICAST(ntohl(addr)))
- return IS_MULTICAST;
- return 0; /* no match at all */
-}
+ if (!suser())
+ return -EPERM;
+ /*
+ * BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
+ * physical address. We can cope with this now.
+ */
+
+ if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+ {
+ int ret;
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ ret = dev->set_mac_address(dev,&ifr.ifr_addr);
+ if (!ret)
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+ return ret;
+ }
+ if(ifr.ifr_addr.sa_family!=AF_INET)
+ return -EINVAL;
-/*
- * Retrieve our own address.
- *
- * Because the loopback address (127.0.0.1) is already recognized
- * automatically, we can use the loopback interface's address as
- * our "primary" interface. This is the address used by IP et
- * al when it doesn't know which address to use (i.e. it does not
- * yet know from or to which interface to go...).
- */
-
-unsigned long ip_my_addr(void)
-{
- struct device *dev;
+ addr = (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr;
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- if (dev->flags & IFF_LOOPBACK)
- return(dev->pa_addr);
- }
- return(0);
-}
+ dev_lock_wait();
+ dev_lock_list();
-/*
- * Find an interface that can handle addresses for a certain address.
- */
+ if (dev->family == AF_INET && addr == dev->pa_addr) {
+ dev_unlock_list();
+ return 0;
+ }
-struct device * ip_dev_bynet(unsigned long addr, unsigned long mask)
-{
- struct device *dev;
- struct device *best_dev = NULL;
- __u32 best_mask = mask;
+ if (dev->flags & IFF_UP)
+ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
- for (dev = dev_base; dev; dev = dev->next)
- {
- if (!(dev->flags & IFF_UP))
- continue;
- if (dev->flags & IFF_POINTOPOINT)
- {
+ /*
+ * if dev is an alias, must rehash to update
+ * address change
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev))
+ net_alias_dev_rehash(dev, &ifr.ifr_addr);
+#endif
+ dev->pa_addr = addr;
+ dev->ip_flags |= IFF_IP_ADDR_OK;
+ dev->ip_flags &= ~(IFF_IP_BRD_OK|IFF_IP_MASK_OK);
+ dev->family = AF_INET;
+ if (dev->flags & IFF_POINTOPOINT) {
+ dev->pa_mask = 0xFFFFFFFF;
+ dev->pa_brdaddr = 0xFFFFFFFF;
+ } else {
+ dev->pa_mask = ip_get_mask(dev->pa_addr);
+ dev->pa_brdaddr = dev->pa_addr|~dev->pa_mask;
+ }
+ if (dev->flags & IFF_UP)
+ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+ dev_unlock_list();
+ return 0;
+
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ break;
+
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ if (!suser())
+ return -EPERM;
+
+ addr = (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr.s_addr;
+
+ if (dev->flags & IFF_UP)
+ ip_rt_change_broadcast(dev, addr);
+ dev->pa_brdaddr = addr;
+ dev->ip_flags |= IFF_IP_BRD_OK;
+ return 0;
+
+ case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_port = 0;
+ break;
+
+ case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
+ if (!suser())
+ return -EPERM;
+ addr = (*(struct sockaddr_in *)&ifr.ifr_dstaddr).sin_addr.s_addr;
if (addr == dev->pa_dstaddr)
- return dev;
- continue;
- }
- if (dev->pa_mask & (addr ^ dev->pa_addr))
- continue;
- if (mask == dev->pa_mask)
- return dev;
- if (best_dev && (best_mask & dev->pa_mask) != best_mask)
- continue;
- best_dev = dev;
- best_mask = dev->pa_mask;
- }
- return best_dev;
-}
+ return 0;
+ if (dev->flags & IFF_UP)
+ ip_rt_change_dstaddr(dev, addr);
+ dev->pa_dstaddr = addr;
+ return 0;
+
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_port = 0;
+ break;
-/*
- * Find the first device with a given source address.
- */
-
-struct device *ip_dev_find(unsigned long addr)
-{
- struct device *dev;
- for(dev = dev_base; dev; dev=dev->next)
- {
- if((dev->flags&IFF_UP) && dev->pa_addr==addr)
- return dev;
- }
- return NULL;
-}
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ if (!suser())
+ return -EPERM;
+ addr = (*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr.s_addr;
-struct device *dev_getbytype(unsigned short type)
-{
- struct device *dev;
+ if (addr == dev->pa_mask) {
+ dev->ip_flags |= IFF_IP_MASK_OK;
+ return 0;
+ }
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- if (dev->type == type && !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
- return(dev);
+ /*
+ * The mask we set must be legal.
+ */
+ if (bad_mask(addr, 0))
+ return -EINVAL;
+ if (addr == htonl(0xFFFFFFFE))
+ return -EINVAL;
+ if (dev->flags & IFF_UP)
+ ip_rt_change_netmask(dev, addr);
+ dev->pa_mask = addr;
+ dev->ip_flags |= IFF_IP_MASK_OK;
+ dev->ip_flags &= ~IFF_IP_BRD_OK;
+ return 0;
+ default:
+ return -EINVAL;
+
}
- return(NULL);
+ if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return 0;
}
-