debug_net:
2 4 "nx_addr_conflict(%p,%p) %d.%d,%d.%d"
+ "nx_addr6_conflict(%u,%u)"
3 8 "inet_bind(%p) %d.%d.%d.%d, %d.%d.%d.%d, %d.%d.%d.%d"
"inet_bind(%p)* %p,%p;%lx %d.%d.%d.%d"
+ "inet6_bind(" NIP6_FMT ", %d): EADDRNOTAVAIL"
+ "inet6_bind(" NIP6_FMT ", %d): PASS"
+ "rawv6_bind(" NIP6_FMT ", %d): EADDRNOTAVAIL"
+ "rawv6_bind(" NIP6_FMT ", %d): PASS"
4 10 "ip_route_connect(%p) %p,%p;%lx"
+ "inet6_create(%p, %d): %d"
+ "tcp_v6_connect(dest ::1 redirected to: " NIP6_FMT ", %d)"
+ "__sock_create(%d, %d, %d, %p, %d): EAFNOSUPPORT"
5 20 "__addr_in_socket(%p,%d.%d.%d.%d) %p:%d.%d.%d.%d %p;%lx"
6 40 "sk,egf: %p [#%d] (from %d)"
"sk,egn: %p [#%d] (from %d)"
"dealloc_nx_info(%p)"
1 2 "alloc_nx_info(%d)*"
"create_nx_info(%d)*"
+ "vc_net_add(%d, data[%d]): %d.%d.%d.%d"
+ "vc_net_add(%d, data[%d]): %d.%d.%d.%d EXISTS"
+ "vc_net_add(%d, data[%d]): %x:%x:%x:%x:%x:%x:%x:%x"
+ "vc_net_add(%d, data[%d]): %x:%x:%x:%x:%x:%x:%x:%x EXISTS"
+ "vc_net_remove(%d, data[%d]): %d.%d.%d.%d"
+ "vc_net_remove(%d, data[%d]): %x:%x:%x:%x:%x:%x:%x:%x"
2 4 "get_nx_info(%p[#%d.%d])"
"put_nx_info(%p[#%d.%d])"
3 8 "claim_nx_info(%p[#%d.%d.%d]) %p"
return 1;
n = nxi->nbipv4;
+ /* Special case accept anything if addr[0] == ::
+ * FIXME either accept this on any position or adjust address
+ * addition in kernel/vserver/network.c */
if (n && (nxi->ipv4[0] == 0))
return 1;
for (i=0; i<n; i++) {
release_nx_info(p->nx_info, p);
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+#include <net/ipv6.h>
+
+static inline int addr6_in_nx_info(struct nx_info *nxi, const struct in6_addr *addr)
+{
+ int n,i;
+
+ if (!nxi)
+ return 1;
+
+ n = nxi->nbipv6;
+ /* Special case accept anything if addr[0] == ::
+ * FIXME either accept this on any position or adjust address
+ * addition in kernel/vserver/network.c */
+ if (n && nxi->ipv6[0].s6_addr32[0] == 0 && nxi->ipv6[0].s6_addr32[1] == 0 &&
+ nxi->ipv6[0].s6_addr32[2] == 0 && nxi->ipv6[0].s6_addr32[3] == 0)
+ return 1;
+ for (i=0; i<n; i++) {
+ if (ipv6_addr_equal(&(nxi->ipv6[i]), addr))
+ return 1;
+ }
+ return 0;
+}
+
+extern int nx_addr6_conflict(struct nx_info *nxi, struct nx_info *nix2);
+
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
#ifdef CONFIG_NETWORK_SECMARK
#define skb_tag secmark
#define VXD_CMIN(n,m) (vx_debug_ ## n > (m))
#define VXD_MASK(n,m) (vx_debug_ ## n & (m))
-#define VXD_QPOS(v,p) (((uint32_t)(v) >> ((p)*8)) & 0xFF)
-#define VXD_QUAD(v) VXD_QPOS(v,0), VXD_QPOS(v,1), \
- VXD_QPOS(v,2), VXD_QPOS(v,3)
-#define VXF_QUAD "%u.%u.%u.%u"
-
#define VXD_DEV(d) (d), (d)->bd_inode->i_ino, \
imajor((d)->bd_inode), iminor((d)->bd_inode)
#define VXF_DEV "%p[%lu,%d:%d]"
#define NX_DYNAMIC_ID ((uint32_t)-1) /* id for dynamic context */
#define NB_IPV4ROOT 16
-
+#define NB_IPV6ROOT 16
/* network flags */
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <asm/atomic.h>
-
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <linux/in6.h>
+#endif
struct nx_info {
struct hlist_node nx_hlist; /* linked list of nxinfos */
/* Used to select the proper source */
/* address for sockets */
__u32 v4_bcast; /* Broadcast address to receive UDP */
-
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ int nbipv6;
+ struct in6_addr ipv6[NB_IPV6ROOT]; /* equivalent to ipv4, but for IPv6 */
+ int prefix6[NB_IPV6ROOT]; /* IPv6 prefix length (in bits) */
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
char nx_name[65]; /* network context name */
};
+struct net_device;
+
+#ifdef CONFIG_IPV6_MODULE
+
+struct nx_ipv6_mod {
+ struct module *owner;
+ /* Function to check if dev in network context through IPv6 address */
+ int (*dev_in_nx_info6)(struct net_device *dev, struct nx_info *nxi);
+};
+
+extern void vc_net_register_ipv6(struct nx_ipv6_mod *modv6);
+
+extern void vc_net_unregister_ipv6(void);
+
+#endif
/* status flags */
extern long vs_net_change(struct nx_info *, unsigned int);
struct in_ifaddr;
-struct net_device;
#ifdef CONFIG_INET
int ifa_in_nx_info(struct in_ifaddr *, struct nx_info *);
#define VCMD_net_add VC_CMD(NETALT, 1, 0)
#define VCMD_net_remove VC_CMD(NETALT, 2, 0)
+#ifdef __KERNEL__
+#include <linux/in.h>
+#include <linux/in6.h>
+#else
+#include <arpa/inet.h>
+#endif /* __KERNEL__ */
+
struct vcmd_net_addr_v0 {
uint16_t type;
uint16_t count;
- uint32_t ip[4];
- uint32_t mask[4];
+ union {
+ struct {
+ struct in_addr ip[4];
+ struct in_addr mask[4];
+ };
+ struct {
+ struct in6_addr ip6;
+ uint32_t prefix;
+ };
+ };
/* more to come */
};
int strict);
extern int ipv6_get_saddr(struct dst_entry *dst,
struct in6_addr *daddr,
- struct in6_addr *saddr);
+ struct in6_addr *saddr,
+ struct nx_info *nxi);
extern int ipv6_dev_get_saddr(struct net_device *dev,
struct in6_addr *daddr,
- struct in6_addr *saddr);
+ struct in6_addr *saddr,
+ struct nx_info *nxi);
extern int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
extern int ipv6_rcv_saddr_equal(const struct sock *sk,
const struct sock *sk2);
struct ipv6_txoptions *opt);
extern int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb);
+extern int ipv6_dev_in_nx_info6(struct net_device *dev, struct nx_info *nxi);
extern int ip6_frag_nqueues;
extern atomic_t ip6_frag_mem;
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+#include <net/addrconf.h>
+
+int nx_addr6_conflict(struct nx_info *nxi, struct nx_info *nxi2)
+{
+ vxdprintk(VXD_CBIT(net, 2), "nx_addr6_conflict(%u,%u)",
+ nxi ? nxi->nx_id : 0, nxi2 ? nxi2->nx_id : 0);
+
+ if (nxi && nxi2 && nxi->nbipv6 > 0 && nxi2->nbipv6 > 0) {
+ int i = 0;
+ for (i = 0; i < nxi->nbipv6; i++)
+ if (addr6_in_nx_info(nxi2, &(nxi->ipv6[i])))
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
int ifa_in_nx_info(struct in_ifaddr *ifa, struct nx_info *nxi)
{
if (!nxi)
return addr_in_nx_info(nxi, ifa->ifa_local);
}
+#ifdef CONFIG_IPV6_MODULE
+
+struct nx_ipv6_mod vc_net_ipv6 = {
+ .dev_in_nx_info6 = NULL,
+ .owner = NULL
+};
+
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
+static atomic_t nx_ipv6mod_lockct = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(nx_ipv6mod_lock);
+
+/* The strategy is: modifications of vc_net_ipv6 are short, do not
+ sleep and veeery rare, but read access should be free of any exclusive
+ locks. (copied from socket.c)
+ This should prevent any possible issues with module unloading!
+ */
+
+static void nx_ipv6mod_write_lock(void)
+{
+ spin_lock(&nx_ipv6mod_lock);
+ while (atomic_read(&nx_ipv6mod_lockct) != 0) {
+ spin_unlock(&nx_ipv6mod_lock);
+
+ yield();
+
+ spin_lock(&nx_ipv6mod_lock);
+ }
+}
+
+static __inline__ void nx_ipv6mod_write_unlock(void)
+{
+ spin_unlock(&nx_ipv6mod_lock);
+}
+
+static __inline__ void nx_ipv6mod_read_lock(void)
+{
+ atomic_inc(&nx_ipv6mod_lockct);
+ spin_unlock_wait(&nx_ipv6mod_lock);
+}
+
+static __inline__ void nx_ipv6mod_read_unlock(void)
+{
+ atomic_dec(&nx_ipv6mod_lockct);
+}
+
+#else
+#define nx_ipv6mod_write_lock() do { } while(0)
+#define nx_ipv6mod_write_unlock() do { } while(0)
+#define nx_ipv6mod_read_lock() do { } while(0)
+#define nx_ipv6mod_read_unlock() do { } while(0)
+#endif
+
+void vc_net_register_ipv6(struct nx_ipv6_mod *modv6) {
+ nx_ipv6mod_write_lock();
+ memcpy(&vc_net_ipv6, modv6, sizeof(struct nx_ipv6_mod));
+ nx_ipv6mod_write_unlock();
+}
+
+void vc_net_unregister_ipv6() {
+ nx_ipv6mod_write_lock();
+ memset(&vc_net_ipv6, 0, sizeof(struct nx_ipv6_mod));
+ nx_ipv6mod_write_unlock();
+}
+
+inline int dev_in_nx_info6(struct net_device *dev, struct nx_info *nxi) {
+ nx_ipv6mod_read_lock();
+ if (try_module_get(vc_net_ipv6.owner)) {
+ if (vc_net_ipv6.dev_in_nx_info6)
+ return vc_net_ipv6.dev_in_nx_info6(dev, nxi);
+ else
+ return 0;
+ module_put(vc_net_ipv6.owner);
+ } else
+ return 0;
+ nx_ipv6mod_read_unlock();
+}
+#endif
+
int dev_in_nx_info(struct net_device *dev, struct nx_info *nxi)
{
- struct in_device *in_dev;
- struct in_ifaddr **ifap;
- struct in_ifaddr *ifa;
int ret = 0;
if (!nxi)
if (!dev)
goto out;
- in_dev = in_dev_get(dev);
- if (!in_dev)
- goto out;
- for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
- ifap = &ifa->ifa_next) {
- if (addr_in_nx_info(nxi, ifa->ifa_local)) {
- ret = 1;
- break;
+ if (nxi->nbipv4 > 0) {
+ struct in_device *in_dev;
+ struct in_ifaddr **ifap;
+ struct in_ifaddr *ifa;
+
+ in_dev = in_dev_get(dev);
+ if (in_dev) {
+ for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
+ ifap = &ifa->ifa_next) {
+ if (addr_in_nx_info(nxi, ifa->ifa_local)) {
+ ret = 1;
+ break;
+ }
+ }
+ in_dev_put(in_dev);
}
}
- in_dev_put(in_dev);
+
+#if defined(CONFIG_IPV6_MODULE)
+ if (ret == 0)
+ ret = dev_in_nx_info6(dev, nxi);
+#elif defined(CONFIG_IPV6)
+ if (ret == 0)
+ ret = ipv6_dev_in_nx_info6(dev, nxi);
+#endif
+
out:
return ret;
}
uint32_t saddr = inet_rcv_saddr(sk);
vxdprintk(VXD_CBIT(net, 5),
- "__addr_in_socket(%p,%d.%d.%d.%d) %p:%d.%d.%d.%d %p;%lx",
- sk, VXD_QUAD(addr), nxi, VXD_QUAD(saddr), sk->sk_socket,
+ "__addr_in_socket(%p," NIPQUAD_FMT ") %p:" NIPQUAD_FMT " %p;%lx",
+ sk, NIPQUAD(addr), nxi, NIPQUAD(saddr), sk->sk_socket,
(sk->sk_socket?sk->sk_socket->flags:0));
if (saddr) {
int nx_addr_conflict(struct nx_info *nxi, uint32_t addr, const struct sock *sk)
{
vxdprintk(VXD_CBIT(net, 2),
- "nx_addr_conflict(%p,%p) %d.%d,%d.%d",
- nxi, sk, VXD_QUAD(addr));
+ "nx_addr_conflict(%p,%p) " NIPQUAD_FMT,
+ nxi, sk, NIPQUAD(addr));
if (addr) {
/* check real address */
return nx_migrate_task(current, nxi);
}
+/*
+ * Lookup address/mask pair in list of v4 addresses
+ * Returns position if found, -1 if not found
+ */
+int vc_net_find_v4(const struct nx_info *nxi, uint32_t addr, uint32_t mask)
+{
+ int ret = nxi->nbipv4 - 1;
+ while (ret >= 0) {
+ if (nxi->ipv4[ret] == addr && nxi->mask[ret] == mask)
+ break;
+ else
+ ret--;
+ }
+ return ret;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/*
+ * Lookup address/prefix pair list of v6 addresses
+ * Returns position if found, -1 if not found
+ */
+int vc_net_find_v6(const struct nx_info *nxi, const struct in6_addr *addr, int prefix)
+{
+ int ret = nxi->nbipv6 - 1;
+ while (ret >= 0) {
+ if (memcmp(&(nxi->ipv6[ret]), addr, sizeof(struct in6_addr)) == 0 && nxi->prefix6[ret] == prefix)
+ break;
+ else
+ ret--;
+ }
+ return ret;
+}
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
int vc_net_add(struct nx_info *nxi, void __user *data)
{
struct vcmd_net_addr_v0 vc_data;
return -EINVAL;
break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case NXA_TYPE_IPV6:
+ /* Note: all 4 items of IP and MASK must be set, but its 1 IPv6 address */
+ if ((vc_data.count != 1))
+ return -EINVAL;
+ break;
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
default:
break;
}
switch (vc_data.type) {
case NXA_TYPE_IPV4:
index = 0;
+ ret = 0;
while ((index < vc_data.count) &&
((pos = nxi->nbipv4) < NB_IPV4ROOT)) {
- nxi->ipv4[pos] = vc_data.ip[index];
- nxi->mask[pos] = vc_data.mask[index];
+ if (vc_net_find_v4(nxi, vc_data.ip[index].s_addr, vc_data.mask[index].s_addr) == -1) {
+ /* Only add if address is new */
+ vxdprintk(VXD_CBIT(nid, 1), "vc_net_add(%d, data[%d]): " NIPQUAD_FMT,
+ nxi->nx_id, index, NIPQUAD(vc_data.ip[index].s_addr));
+ nxi->ipv4[pos] = vc_data.ip[index].s_addr;
+ nxi->mask[pos] = vc_data.mask[index].s_addr;
+ nxi->nbipv4++;
+ ret++;
+ } else
+ vxdprintk(VXD_CBIT(nid, 1), "vc_net_add(%d, data[%d]): " NIPQUAD_FMT " EXISTS",
+ nxi->nx_id, index, NIPQUAD(vc_data.ip[index].s_addr));
index++;
- nxi->nbipv4++;
}
ret = index;
break;
case NXA_TYPE_IPV4|NXA_MOD_BCAST:
- nxi->v4_bcast = vc_data.ip[0];
+ nxi->v4_bcast = vc_data.ip[0].s_addr;
ret = 1;
break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case NXA_TYPE_IPV6:
+ index = 0;
+ ret = 0;
+ while (nxi->nbipv6 < NB_IPV6ROOT && index < vc_data.count) {
+ if (vc_net_find_v6(nxi, &vc_data.ip6, vc_data.prefix) == -1) {
+ /* Only add if address is new */
+ vxdprintk(VXD_CBIT(nid, 1), "vc_net_add(%d, data[%d]): " NIP6_FMT,
+ nxi->nx_id, index, NIP6(vc_data.ip6));
+ nxi->ipv6[nxi->nbipv6] = vc_data.ip6;
+ nxi->prefix6[nxi->nbipv6] = vc_data.prefix;
+ nxi->nbipv6++;
+ ret++;
+ } else
+ vxdprintk(VXD_CBIT(nid, 1), "vc_net_add(%d, data[%d]): " NIP6_FMT " EXISTS",
+ nxi->nx_id, index, NIP6(vc_data.ip6));
+ index++;
+ }
+ break;
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
default:
ret = -EINVAL;
break;
int vc_net_remove(struct nx_info * nxi, void __user *data)
{
struct vcmd_net_addr_v0 vc_data;
+ int index, pos, ret = 0;
if (data && copy_from_user (&vc_data, data, sizeof(vc_data)))
return -EFAULT;
switch (vc_data.type) {
case NXA_TYPE_ANY:
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ ret = nxi->nbipv6;
+ nxi->nbipv6 = 0;
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+ ret += nxi->nbipv4;
nxi->nbipv4 = 0;
break;
+ case NXA_TYPE_IPV4:
+ index = 0;
+ ret = 0;
+ while (index < vc_data.count) {
+ pos = vc_net_find_v4(nxi, vc_data.ip[index].s_addr, vc_data.mask[index].s_addr);
+ if (pos >= 0) {
+ nxi->nbipv4--;
+ ret++;
+ vxdprintk(VXD_CBIT(nid, 1), "vc_net_remove(%d, data[%d]): " NIPQUAD_FMT,
+ nxi->nx_id, index, NIPQUAD(vc_data.ip[index].s_addr));
+ }
+ while (pos >= 0 && pos < nxi->nbipv4) {
+ nxi->ipv4[pos] = nxi->ipv4[pos+1];
+ nxi->mask[pos] = nxi->mask[pos+1];
+ pos++;
+ }
+ index++;
+ }
+ break;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case NXA_TYPE_IPV6:
+ index = 0;
+ ret = 0;
+ while (index < vc_data.count) {
+ pos = vc_net_find_v6(nxi, &(vc_data.ip6), vc_data.prefix);
+ if (pos >= 0) {
+ nxi->nbipv6--;
+ ret++;
+ vxdprintk(VXD_CBIT(nid, 1), "vc_net_remove(%d, data[%d]): " NIP6_FMT " EXISTS",
+ nxi->nx_id, index, NIP6(vc_data.ip6));
+ }
+ while (pos >= 0 && pos < nxi->nbipv6) {
+ nxi->ipv6[pos] = nxi->ipv6[pos+1];
+ nxi->prefix6[pos] = nxi->prefix6[pos+1];
+ pos++;
+ }
+ index++;
+ }
+ break;
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
- return 0;
+ return ret;
}
int vc_get_nflags(struct nx_info *nxi, void __user *data)
EXPORT_SYMBOL_GPL(free_nx_info);
EXPORT_SYMBOL_GPL(unhash_nx_info);
+#ifdef CONFIG_IPV6_MODULE
+EXPORT_SYMBOL_GPL(nx_addr6_conflict);
+EXPORT_SYMBOL_GPL(vc_net_register_ipv6);
+EXPORT_SYMBOL_GPL(vc_net_unregister_ipv6);
+#elif defined(CONFIG_IPV6)
+EXPORT_SYMBOL_GPL(nx_addr6_conflict);
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
"%d:\t" NIPQUAD_FMT "/" NIPQUAD_FMT "\n", i,
NIPQUAD(nxi->ipv4[i]), NIPQUAD(nxi->mask[i]));
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ for (i=0; i<nxi->nbipv6;i++) {
+ length += sprintf(buffer + length,
+ "%d:\t" NIP6_FMT "/%d\n", i,
+ NIP6(nxi->ipv6[i]), nxi->prefix6[i]);
+ }
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
return length;
}
s_addr2 = 0xffffffffl;
vxdprintk(VXD_CBIT(net, 3),
- "inet_bind(%p)* %p,%p;%lx %d.%d.%d.%d",
+ "inet_bind(%p)* %p,%p;%lx " NIPQUAD_FMT,
sk, sk->sk_nx_info, sk->sk_socket,
(sk->sk_socket?sk->sk_socket->flags:0),
- VXD_QUAD(s_addr));
+ NIPQUAD(s_addr));
if (nxi) {
__u32 v4_bcast = nxi->v4_bcast;
__u32 ipv4root = nxi->ipv4[0];
chk_addr_ret = inet_addr_type(s_addr);
vxdprintk(VXD_CBIT(net, 3),
- "inet_bind(%p) %d.%d.%d.%d, %d.%d.%d.%d, %d.%d.%d.%d",
- sk, VXD_QUAD(s_addr), VXD_QUAD(s_addr1), VXD_QUAD(s_addr2));
+ "inet_bind(%p) " NIPQUAD_FMT ", " NIPQUAD_FMT ", " NIPQUAD_FMT,
+ sk, NIPQUAD(s_addr), NIPQUAD(s_addr1), NIPQUAD(s_addr2));
/* Not specified by any standard per-se, however it breaks too
* many applications when removed. It is unfortunate since
}
int ipv6_dev_get_saddr(struct net_device *daddr_dev,
- struct in6_addr *daddr, struct in6_addr *saddr)
+ struct in6_addr *daddr, struct in6_addr *saddr, struct nx_info *nxi)
{
struct ipv6_saddr_score hiscore;
struct inet6_ifaddr *ifa_result = NULL;
score.addr_type = __ipv6_addr_type(&ifa->addr);
+ /* Linux-VServer: only take into account addresses
+ available in guest context! */
+ if (!addr6_in_nx_info(nxi, &ifa->addr))
+ continue;
+
/* Rule 0:
* - Tentative Address (RFC2462 section 5.4)
* - A tentative address is not considered
int ipv6_get_saddr(struct dst_entry *dst,
- struct in6_addr *daddr, struct in6_addr *saddr)
+ struct in6_addr *daddr, struct in6_addr *saddr, struct nx_info *nxi)
{
- return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr);
+ return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr, nxi);
}
int addr_type = ipv6_addr_type(sk_rcv_saddr6);
int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
- if (!sk2_rcv_saddr && !sk_ipv6only)
- return 1;
+ if (sk->sk_nid != sk2->sk_nid) {
+ // either socket is in host context and bound to everything
+ if (sk->sk_nx_info == NULL && addr_type == IPV6_ADDR_ANY && !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
+ return 1;
+ if (sk2->sk_nx_info == NULL && addr_type2 == IPV6_ADDR_ANY && !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
+ return 1;
+ // If both addresses are equal
+ if (sk2_rcv_saddr6 && ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
+ return 1;
+ // If one socket bound to everything and the addr bound by other socket is in first context
+ if (addr_type2 == IPV6_ADDR_ANY && !(addr_type == IPV6_ADDR_ANY || addr_type == IPV6_ADDR_MAPPED) &&
+ addr6_in_nx_info(sk2->sk_nx_info, sk_rcv_saddr6))
+ return 1;
+ if (addr_type == IPV6_ADDR_ANY && !(addr_type2 == IPV6_ADDR_ANY || addr_type2 == IPV6_ADDR_MAPPED) &&
+ sk2_rcv_saddr6 && addr6_in_nx_info(sk->sk_nx_info, sk2_rcv_saddr6))
+ return 1;
+ // both sockets are in guests and bound to everything with addr-overlap between guests
+ if (sk->sk_nx_info && sk2->sk_nx_info && addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY &&
+ nx_addr6_conflict(sk->sk_nx_info, sk2->sk_nx_info))
+ return 1;
+ // TODO: handle the IPv4 addresses mapped in IPv6 completely...
+ } else {
+ if (!sk2_rcv_saddr && !sk_ipv6only)
+ return 1;
- if (addr_type2 == IPV6_ADDR_ANY &&
- !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
- return 1;
+ if (addr_type2 == IPV6_ADDR_ANY &&
+ !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
+ return 1;
- if (addr_type == IPV6_ADDR_ANY &&
- !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
- return 1;
+ if (addr_type == IPV6_ADDR_ANY &&
+ !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
+ return 1;
- if (sk2_rcv_saddr6 &&
- ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
- return 1;
-
- if (addr_type == IPV6_ADDR_MAPPED &&
- !sk2_ipv6only &&
- (!sk2_rcv_saddr || !sk_rcv_saddr || sk_rcv_saddr == sk2_rcv_saddr))
- return 1;
+ if (sk2_rcv_saddr6 &&
+ ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
+ return 1;
+ if (addr_type == IPV6_ADDR_MAPPED &&
+ !sk2_ipv6only &&
+ (!sk2_rcv_saddr || !sk_rcv_saddr || sk_rcv_saddr == sk2_rcv_saddr))
+ return 1;
+ }
return 0;
}
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
- /* no ipv6 inside a vserver for now */
- if (nx_check(0, VS_ADMIN|VS_WATCH))
+ /* Only addresses visible to vserver context */
+ if (nx_check(0, VS_ADMIN|VS_WATCH) ||
+ addr6_in_nx_info(current_nx_info(), &ifp->addr))
seq_printf(seq,
NIP6_SEQFMT " %02x %02x %02x %02x %8s\n",
NIP6(ifp->addr),
struct ifmcaddr6 *ifmca;
struct ifacaddr6 *ifaca;
- /* no ipv6 inside a vserver for now */
- if (skb->sk && skb->sk->sk_vx_info)
+ /* no ipv6 inside a vserver if no addr set */
+ if (skb->sk && skb->sk->sk_nx_info && skb->sk->sk_nx_info->nbipv6 == 0)
return skb->len;
s_idx = cb->args[0];
ifa = ifa->if_next, ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
+ if (skb->sk && skb->sk->sk_nx_info && !addr6_in_nx_info(skb->sk->sk_nx_info, &ifa->addr))
+ continue; /* Skip addresses not in nx */
if ((err = inet6_fill_ifaddr(skb, ifa,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWADDR,
ifmca = ifmca->next, ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
+ if (skb->sk && skb->sk->sk_nx_info && !addr6_in_nx_info(skb->sk->sk_nx_info, &ifmca->mca_addr))
+ continue; /* Skip addresses not in nx */
if ((err = inet6_fill_ifmcaddr(skb, ifmca,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_GETMULTICAST,
ifaca = ifaca->aca_next, ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
+ if (skb->sk && skb->sk->sk_nx_info && !addr6_in_nx_info(skb->sk->sk_nx_info, &ifaca->aca_addr))
+ continue; /* Skip addresses not in nx */
if ((err = inet6_fill_ifacaddr(skb, ifaca,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_GETANYCAST,
struct net_device *dev;
struct inet6_dev *idev;
- /* no ipv6 inside a vserver for now */
- if (skb->sk && skb->sk->sk_vx_info)
+ /* no ipv6 inside a vserver if no addr set */
+ if (skb->sk && skb->sk->sk_nx_info && skb->sk->sk_nx_info->nbipv6 == 0)
return skb->len;
read_lock(&dev_base_lock);
for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
if (idx < s_idx)
continue;
+ if (skb->sk && skb->sk->sk_nx_info && !ipv6_dev_in_nx_info6(dev, skb->sk->sk_nx_info))
+ continue;
if ((idev = in6_dev_get(dev)) == NULL)
continue;
err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/init.h>
+#include <linux/vs_context.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
}
err = -EPERM;
+ if ((protocol == IPPROTO_ICMPV6) && vx_ccaps(VXC_RAW_ICMP))
+ goto override;
if (answer->capability > 0 && !capable(answer->capability))
goto out_rcu_unlock;
-
+override:
sock->ops = answer->ops;
answer_prot = answer->prot;
answer_no_check = answer->no_check;
}
}
out:
+ vxdprintk(VXD_CBIT(net, 4), "inet6_create(%p, %d): %d",
+ sock, protocol, err);
return err;
out_rcu_unlock:
rcu_read_unlock();
}
}
+ /* VServer: Only accept if we have the address available */
+ if (sk->sk_nx_info && !addr6_in_nx_info(sk->sk_nx_info, &addr->sin6_addr)) {
+ vxdprintk(VXD_CBIT(net, 3), "inet6_bind(" NIP6_FMT ", %d): EADDRNOTAVAIL",
+ NIP6(addr->sin6_addr), sk->sk_nx_info->nx_id);
+ err = -EADDRNOTAVAIL;
+ if (dev)
+ dev_put(dev);
+ goto out;
+ } else
+ vxdprintk(VXD_CBIT(net, 3), "inet6_bind(" NIP6_FMT ", %d): PASS",
+ NIP6(addr->sin6_addr), sk->sk_nx_info ? sk->sk_nx_info->nx_id : 0);
+
/* ipv4 addr of the socket is invalid. Only the
* unspecified and mapped address have a v4 equivalent.
*/
EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
+int ipv6_dev_in_nx_info6(struct net_device *dev, struct nx_info *nxi) {
+ int ret = 0;
+
+ if (nxi->nbipv6 > 0) {
+ struct inet6_dev *in_dev;
+ struct inet6_ifaddr **ifap;
+ struct inet6_ifaddr *ifa;
+
+ in_dev = in6_dev_get(dev);
+ if (in_dev) {
+ for (ifap = &in_dev->addr_list; (ifa = *ifap) != NULL;
+ ifap = &ifa->if_next) {
+ if (addr6_in_nx_info(nxi, &(ifa->addr))) {
+ ret = 1;
+ break;
+ }
+ }
+ in6_dev_put(in_dev);
+ }
+ }
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipv6_dev_in_nx_info6);
+
int
snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign)
{
snmp6_mib_free((void **)udplite_stats_in6);
}
+#ifdef CONFIG_IPV6_MODULE
+static void vc_net_register_init(void)
+{
+ struct nx_ipv6_mod mymod = {
+ .dev_in_nx_info6 = ipv6_dev_in_nx_info6,
+ .owner = THIS_MODULE
+ };
+ vc_net_register_ipv6(&mymod);
+}
+#endif
+
static int __init inet6_init(void)
{
struct sk_buff *dummy_skb;
tcpv6_init();
ipv6_packet_init();
+
+#ifdef CONFIG_IPV6_MODULE
+ vc_net_register_init();
+#endif
+
err = 0;
out:
return err;
{
/* First of all disallow new sockets creation. */
sock_unregister(PF_INET6);
+#ifdef CONFIG_IPV6_MODULE
+ vc_net_unregister_ipv6();
+#endif
#ifdef CONFIG_PROC_FS
if6_proc_exit();
ac6_proc_exit();
}
EXPORT_SYMBOL(__inet6_lookup_established);
+/*
+ * Check if a given address matches for an inet socket
+ *
+ * nxi: the socket's nx_info if any
+ * addr: to be verified address
+ * saddr: socket addresses
+ */
+static inline int inet6_addr_match(
+ struct nx_info *nxi,
+ const struct in6_addr *addr,
+ const struct in6_addr *saddr)
+{
+ if ((addr->s6_addr32[0] || addr->s6_addr32[1] || addr->s6_addr32[2] || addr->s6_addr32[3]) &&
+ memcmp(saddr,addr, sizeof(struct in6_addr)) == 0)
+ return 1;
+ if (!(saddr->s6_addr32[0] || saddr->s6_addr32[1] || saddr->s6_addr32[2] || saddr->s6_addr32[3]))
+ return addr6_in_nx_info(nxi, addr);
+ return 0;
+}
+
struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
const struct in6_addr *daddr,
const unsigned short hnum, const int dif)
continue;
score++;
}
+ if (!inet6_addr_match(sk->sk_nx_info, daddr, &(np->rcv_saddr))) {
+ /* No, this address is not available for guest */
+ continue;
+ }
if (score == 3) {
result = sk;
break;
#include <linux/in6.h>
#include <linux/tcp.h>
#include <linux/route.h>
-#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
goto out_err_release;
if (ipv6_addr_any(&fl->fl6_src)) {
- err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
+ err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src, sk->sk_nx_info);
+
if (err)
goto out_err_release;
}
src_addr = solicited_addr;
in6_ifa_put(ifp);
} else {
- if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
+ if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr, NULL))
return;
src_addr = &tmpaddr;
}
continue;
if (!ipv6_addr_any(&np->rcv_saddr)) {
- if (ipv6_addr_equal(&np->rcv_saddr, loc_addr))
+ if (ipv6_addr_equal(&np->rcv_saddr, loc_addr) &&
+ addr6_in_nx_info(sk->sk_nx_info, loc_addr))
goto found;
if (is_multicast &&
inet6_mc_check(sk, loc_addr, rmt_addr))
goto out;
}
}
-
+
+ /* VServer: Only accept if we have the address available */
+ if (sk->sk_nx_info && !addr6_in_nx_info(sk->sk_nx_info, &addr->sin6_addr)) {
+ vxdprintk(VXD_CBIT(net, 3), "rawv6_bind(" NIP6_FMT ", %d): EADDRNOTAVAIL",
+ NIP6(addr->sin6_addr), sk->sk_nx_info->nx_id);
+ err = -EADDRNOTAVAIL;
+ if (dev)
+ dev_put(dev);
+ goto out;
+ } else
+ vxdprintk(VXD_CBIT(net, 3), "rawv6_bind(" NIP6_FMT ", %d): PASS",
+ NIP6(addr->sin6_addr), sk->sk_nx_info ? sk->sk_nx_info->nx_id : 0);
+
/* ipv4 addr of the socket is invalid. Only the
* unspecified and mapped address have a v4 equivalent.
*/
if (err)
goto error_fault;
+ err = -EPERM;
+ if (!nx_check(0, VS_ADMIN) && !capable(CAP_NET_RAW)
+ && (!addr6_in_nx_info(sk->sk_nx_info, &iph->saddr)))
+ goto error_free;
+
IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
error_fault:
err = -EFAULT;
+error_free:
kfree_skb(skb);
error:
IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
NLA_PUT_U32(skb, RTA_IIF, iif);
else if (dst) {
struct in6_addr saddr_buf;
- if (ipv6_get_saddr(&rt->u.dst, dst, &saddr_buf) == 0)
+ if (ipv6_get_saddr(&rt->u.dst, dst, &saddr_buf, NULL) == 0)
NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
}
* connect() to INADDR_ANY means loopback (BSD'ism).
*/
- if(ipv6_addr_any(&usin->sin6_addr))
- usin->sin6_addr.s6_addr[15] = 0x1;
+ if(ipv6_addr_any(&usin->sin6_addr)) {
+ if (sk->sk_nx_info) {
+ if (sk->sk_nx_info->nbipv6) {
+ /* Linux-VServer: redirects to first IPv6 address set for guest
+ TODO: search for existing loopback address in guest context... */
+ usin->sin6_addr = sk->sk_nx_info->ipv6[0];
+ vxdprintk(VXD_CBIT(net, 4), "tcp_v6_connect(dest ::1 redirected to: " NIP6_FMT ", %d)",
+ NIP6(usin->sin6_addr), sk->sk_nx_info->nx_id);
+ } else
+ return -ENETUNREACH;
+ } else
+ usin->sin6_addr.s6_addr[15] = 0x1;
+ }
addr_type = ipv6_addr_type(&usin->sin6_addr);
return udp_get_port(sk, snum, ipv6_rcv_saddr_equal);
}
+static inline int udp_v6_in_list(struct nx_info *nx_info, struct in6_addr *addr)
+{
+ int n = nx_info->nbipv6;
+ int i;
+
+ for (i=0; i<n; i++)
+ if (ipv6_addr_equal(&nx_info->ipv6[i], addr))
+ return 1;
+ return 0;
+}
+
static struct sock *__udp6_lib_lookup(struct in6_addr *saddr, __be16 sport,
struct in6_addr *daddr, __be16 dport,
int dif, struct hlist_head udptable[])
if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
continue;
score++;
+ } else if (sk->sk_nx_info) {
+ if (udp_v6_in_list(sk->sk_nx_info, daddr))
+ score++;
+ else
+ continue;
}
if (!ipv6_addr_any(&np->daddr)) {
if (!ipv6_addr_equal(&np->daddr, saddr))
if (!xfrm6_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) {
ipv6_get_saddr(&rt->u.dst, (struct in6_addr *)&daddr->a6,
- (struct in6_addr *)&saddr->a6);
+ (struct in6_addr *)&saddr->a6, NULL);
dst_release(&rt->u.dst);
return 0;
}
__FUNCTION__, asoc, dst, NIP6(daddr->v6.sin6_addr));
if (!asoc) {
- ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr);
+ ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr, NULL);
SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n",
NIP6(saddr->v6.sin6_addr));
return;
#include <linux/netfilter.h>
#include <linux/vs_base.h>
#include <linux/vs_socket.h>
+#include <linux/vs_network.h>
static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
if (type < 0 || type >= SOCK_MAX)
return -EINVAL;
- /* disable IPv6 inside vservers for now */
- if (family == PF_INET6 && !nx_check(0, VS_ADMIN))
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ /* disable IPv6 inside vservers that have no IPv6 address configured */
+ if (family == PF_INET6 && current_nx_info() && current_nx_info()->nbipv6 == 0) {
+ vxdprintk(VXD_CBIT(net, 4), "__sock_create(%d, %d, %d, %p, %d): EAFNOSUPPORT",
+ family, type, protocol, res, kern);
return -EAFNOSUPPORT;
+ }
+#endif
/* Compatibility.