|
@@ -24,6 +24,10 @@
|
|
|
#include <linux/inet.h>
|
|
|
#include <net/dst.h>
|
|
|
#include <net/route.h>
|
|
|
+#include <net/ipv6.h>
|
|
|
+#include <net/ip6_route.h>
|
|
|
+#include <net/addrconf.h>
|
|
|
+
|
|
|
#include <linux/inetdevice.h> /* ip_dev_find */
|
|
|
#include <linux/module.h>
|
|
|
#include <net/tcp.h>
|
|
@@ -193,8 +197,8 @@ struct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_lldev);
|
|
|
|
|
|
-static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
|
|
|
- int *port)
|
|
|
+struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
|
|
|
+ int *port)
|
|
|
{
|
|
|
struct net_device *vdev = NULL;
|
|
|
struct cxgbi_device *cdev, *tmp;
|
|
@@ -224,6 +228,40 @@ static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
|
|
|
"ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
|
|
|
return NULL;
|
|
|
}
|
|
|
+EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
|
|
|
+
|
|
|
+static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
|
|
|
+ int *port)
|
|
|
+{
|
|
|
+ struct net_device *vdev = NULL;
|
|
|
+ struct cxgbi_device *cdev, *tmp;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (ndev->priv_flags & IFF_802_1Q_VLAN) {
|
|
|
+ vdev = ndev;
|
|
|
+ ndev = vlan_dev_real_dev(ndev);
|
|
|
+ pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&cdev_mutex);
|
|
|
+ list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
|
|
|
+ for (i = 0; i < cdev->nports; i++) {
|
|
|
+ if (!memcmp(ndev->dev_addr, cdev->ports[i]->dev_addr,
|
|
|
+ MAX_ADDR_LEN)) {
|
|
|
+ cdev->hbas[i]->vdev = vdev;
|
|
|
+ mutex_unlock(&cdev_mutex);
|
|
|
+ if (port)
|
|
|
+ *port = i;
|
|
|
+ return cdev;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mutex_unlock(&cdev_mutex);
|
|
|
+ log_debug(1 << CXGBI_DBG_DEV,
|
|
|
+ "ndev 0x%p, %s, NO match mac found.\n",
|
|
|
+ ndev, ndev->name);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
|
|
|
void cxgbi_hbas_remove(struct cxgbi_device *cdev)
|
|
|
{
|
|
@@ -320,6 +358,7 @@ static int sock_get_port(struct cxgbi_sock *csk)
|
|
|
struct cxgbi_ports_map *pmap = &cdev->pmap;
|
|
|
unsigned int start;
|
|
|
int idx;
|
|
|
+ __be16 *port;
|
|
|
|
|
|
if (!pmap->max_connect) {
|
|
|
pr_err("cdev 0x%p, p#%u %s, NO port map.\n",
|
|
@@ -327,9 +366,14 @@ static int sock_get_port(struct cxgbi_sock *csk)
|
|
|
return -EADDRNOTAVAIL;
|
|
|
}
|
|
|
|
|
|
- if (csk->saddr.sin_port) {
|
|
|
+ if (csk->csk_family == AF_INET)
|
|
|
+ port = &csk->saddr.sin_port;
|
|
|
+ else /* ipv6 */
|
|
|
+ port = &csk->saddr6.sin6_port;
|
|
|
+
|
|
|
+ if (*port) {
|
|
|
pr_err("source port NON-ZERO %u.\n",
|
|
|
- ntohs(csk->saddr.sin_port));
|
|
|
+ ntohs(*port));
|
|
|
return -EADDRINUSE;
|
|
|
}
|
|
|
|
|
@@ -347,8 +391,7 @@ static int sock_get_port(struct cxgbi_sock *csk)
|
|
|
idx = 0;
|
|
|
if (!pmap->port_csk[idx]) {
|
|
|
pmap->used++;
|
|
|
- csk->saddr.sin_port =
|
|
|
- htons(pmap->sport_base + idx);
|
|
|
+ *port = htons(pmap->sport_base + idx);
|
|
|
pmap->next = idx;
|
|
|
pmap->port_csk[idx] = csk;
|
|
|
spin_unlock_bh(&pmap->lock);
|
|
@@ -374,16 +417,22 @@ static void sock_put_port(struct cxgbi_sock *csk)
|
|
|
{
|
|
|
struct cxgbi_device *cdev = csk->cdev;
|
|
|
struct cxgbi_ports_map *pmap = &cdev->pmap;
|
|
|
+ __be16 *port;
|
|
|
|
|
|
- if (csk->saddr.sin_port) {
|
|
|
- int idx = ntohs(csk->saddr.sin_port) - pmap->sport_base;
|
|
|
+ if (csk->csk_family == AF_INET)
|
|
|
+ port = &csk->saddr.sin_port;
|
|
|
+ else /* ipv6 */
|
|
|
+ port = &csk->saddr6.sin6_port;
|
|
|
|
|
|
- csk->saddr.sin_port = 0;
|
|
|
+ if (*port) {
|
|
|
+ int idx = ntohs(*port) - pmap->sport_base;
|
|
|
+
|
|
|
+ *port = 0;
|
|
|
if (idx < 0 || idx >= pmap->max_connect) {
|
|
|
pr_err("cdev 0x%p, p#%u %s, port %u OOR.\n",
|
|
|
cdev, csk->port_id,
|
|
|
cdev->ports[csk->port_id]->name,
|
|
|
- ntohs(csk->saddr.sin_port));
|
|
|
+ ntohs(*port));
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -479,17 +528,11 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
|
|
|
int port = 0xFFFF;
|
|
|
int err = 0;
|
|
|
|
|
|
- if (daddr->sin_family != AF_INET) {
|
|
|
- pr_info("address family 0x%x NOT supported.\n",
|
|
|
- daddr->sin_family);
|
|
|
- err = -EAFNOSUPPORT;
|
|
|
- goto err_out;
|
|
|
- }
|
|
|
-
|
|
|
rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0, daddr->sin_port, 0);
|
|
|
if (!rt) {
|
|
|
pr_info("no route to ipv4 0x%x, port %u.\n",
|
|
|
- daddr->sin_addr.s_addr, daddr->sin_port);
|
|
|
+ be32_to_cpu(daddr->sin_addr.s_addr),
|
|
|
+ be16_to_cpu(daddr->sin_port));
|
|
|
err = -ENETUNREACH;
|
|
|
goto err_out;
|
|
|
}
|
|
@@ -537,9 +580,12 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
|
|
|
csk->port_id = port;
|
|
|
csk->mtu = mtu;
|
|
|
csk->dst = dst;
|
|
|
+
|
|
|
+ csk->csk_family = AF_INET;
|
|
|
csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr;
|
|
|
csk->daddr.sin_port = daddr->sin_port;
|
|
|
csk->daddr.sin_family = daddr->sin_family;
|
|
|
+ csk->saddr.sin_family = daddr->sin_family;
|
|
|
csk->saddr.sin_addr.s_addr = fl4.saddr;
|
|
|
neigh_release(n);
|
|
|
|
|
@@ -556,6 +602,121 @@ err_out:
|
|
|
return ERR_PTR(err);
|
|
|
}
|
|
|
|
|
|
+static struct rt6_info *find_route_ipv6(const struct in6_addr *saddr,
|
|
|
+ const struct in6_addr *daddr)
|
|
|
+{
|
|
|
+ struct flowi6 fl;
|
|
|
+
|
|
|
+ if (saddr)
|
|
|
+ memcpy(&fl.saddr, saddr, sizeof(struct in6_addr));
|
|
|
+ if (daddr)
|
|
|
+ memcpy(&fl.daddr, daddr, sizeof(struct in6_addr));
|
|
|
+ return (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
|
|
|
+}
|
|
|
+
|
|
|
+static struct cxgbi_sock *cxgbi_check_route6(struct sockaddr *dst_addr)
|
|
|
+{
|
|
|
+ struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)dst_addr;
|
|
|
+ struct dst_entry *dst;
|
|
|
+ struct net_device *ndev;
|
|
|
+ struct cxgbi_device *cdev;
|
|
|
+ struct rt6_info *rt = NULL;
|
|
|
+ struct neighbour *n;
|
|
|
+ struct in6_addr pref_saddr;
|
|
|
+ struct cxgbi_sock *csk = NULL;
|
|
|
+ unsigned int mtu = 0;
|
|
|
+ int port = 0xFFFF;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ rt = find_route_ipv6(NULL, &daddr6->sin6_addr);
|
|
|
+
|
|
|
+ if (!rt) {
|
|
|
+ pr_info("no route to ipv6 %pI6 port %u\n",
|
|
|
+ daddr6->sin6_addr.s6_addr,
|
|
|
+ be16_to_cpu(daddr6->sin6_port));
|
|
|
+ err = -ENETUNREACH;
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+
|
|
|
+ dst = &rt->dst;
|
|
|
+
|
|
|
+ n = dst_neigh_lookup(dst, &daddr6->sin6_addr);
|
|
|
+
|
|
|
+ if (!n) {
|
|
|
+ pr_info("%pI6, port %u, dst no neighbour.\n",
|
|
|
+ daddr6->sin6_addr.s6_addr,
|
|
|
+ be16_to_cpu(daddr6->sin6_port));
|
|
|
+ err = -ENETUNREACH;
|
|
|
+ goto rel_rt;
|
|
|
+ }
|
|
|
+ ndev = n->dev;
|
|
|
+
|
|
|
+ if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
|
|
|
+ pr_info("multi-cast route %pI6 port %u, dev %s.\n",
|
|
|
+ daddr6->sin6_addr.s6_addr,
|
|
|
+ ntohs(daddr6->sin6_port), ndev->name);
|
|
|
+ err = -ENETUNREACH;
|
|
|
+ goto rel_rt;
|
|
|
+ }
|
|
|
+
|
|
|
+ cdev = cxgbi_device_find_by_netdev(ndev, &port);
|
|
|
+ if (!cdev)
|
|
|
+ cdev = cxgbi_device_find_by_mac(ndev, &port);
|
|
|
+ if (!cdev) {
|
|
|
+ pr_info("dst %pI6 %s, NOT cxgbi device.\n",
|
|
|
+ daddr6->sin6_addr.s6_addr, ndev->name);
|
|
|
+ err = -ENETUNREACH;
|
|
|
+ goto rel_rt;
|
|
|
+ }
|
|
|
+ log_debug(1 << CXGBI_DBG_SOCK,
|
|
|
+ "route to %pI6 :%u, ndev p#%d,%s, cdev 0x%p.\n",
|
|
|
+ daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), port,
|
|
|
+ ndev->name, cdev);
|
|
|
+
|
|
|
+ csk = cxgbi_sock_create(cdev);
|
|
|
+ if (!csk) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto rel_rt;
|
|
|
+ }
|
|
|
+ csk->cdev = cdev;
|
|
|
+ csk->port_id = port;
|
|
|
+ csk->mtu = mtu;
|
|
|
+ csk->dst = dst;
|
|
|
+
|
|
|
+ if (ipv6_addr_any(&rt->rt6i_prefsrc.addr)) {
|
|
|
+ struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
|
|
|
+
|
|
|
+ err = ipv6_dev_get_saddr(&init_net, idev ? idev->dev : NULL,
|
|
|
+ &daddr6->sin6_addr, 0, &pref_saddr);
|
|
|
+ if (err) {
|
|
|
+ pr_info("failed to get source address to reach %pI6\n",
|
|
|
+ &daddr6->sin6_addr);
|
|
|
+ goto rel_rt;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ pref_saddr = rt->rt6i_prefsrc.addr;
|
|
|
+ }
|
|
|
+
|
|
|
+ csk->csk_family = AF_INET6;
|
|
|
+ csk->daddr6.sin6_addr = daddr6->sin6_addr;
|
|
|
+ csk->daddr6.sin6_port = daddr6->sin6_port;
|
|
|
+ csk->daddr6.sin6_family = daddr6->sin6_family;
|
|
|
+ csk->saddr6.sin6_addr = pref_saddr;
|
|
|
+
|
|
|
+ neigh_release(n);
|
|
|
+ return csk;
|
|
|
+
|
|
|
+rel_rt:
|
|
|
+ if (n)
|
|
|
+ neigh_release(n);
|
|
|
+
|
|
|
+ ip6_rt_put(rt);
|
|
|
+ if (csk)
|
|
|
+ cxgbi_sock_closed(csk);
|
|
|
+err_out:
|
|
|
+ return ERR_PTR(err);
|
|
|
+}
|
|
|
+
|
|
|
void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn,
|
|
|
unsigned int opt)
|
|
|
{
|
|
@@ -2194,6 +2355,34 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cxgbi_set_conn_param);
|
|
|
|
|
|
+static inline int csk_print_port(struct cxgbi_sock *csk, char *buf)
|
|
|
+{
|
|
|
+ int len;
|
|
|
+
|
|
|
+ cxgbi_sock_get(csk);
|
|
|
+ len = sprintf(buf, "%hu\n", ntohs(csk->daddr.sin_port));
|
|
|
+ cxgbi_sock_put(csk);
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int csk_print_ip(struct cxgbi_sock *csk, char *buf)
|
|
|
+{
|
|
|
+ int len;
|
|
|
+
|
|
|
+ cxgbi_sock_get(csk);
|
|
|
+ if (csk->csk_family == AF_INET)
|
|
|
+ len = sprintf(buf, "%pI4",
|
|
|
+ &csk->daddr.sin_addr.s_addr);
|
|
|
+ else
|
|
|
+ len = sprintf(buf, "%pI6",
|
|
|
+ &csk->daddr6.sin6_addr);
|
|
|
+
|
|
|
+ cxgbi_sock_put(csk);
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
|
|
|
char *buf)
|
|
|
{
|
|
@@ -2447,7 +2636,17 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- csk = cxgbi_check_route(dst_addr);
|
|
|
+ if (dst_addr->sa_family == AF_INET) {
|
|
|
+ csk = cxgbi_check_route(dst_addr);
|
|
|
+ } else if (dst_addr->sa_family == AF_INET6) {
|
|
|
+ csk = cxgbi_check_route6(dst_addr);
|
|
|
+ } else {
|
|
|
+ pr_info("address family 0x%x NOT supported.\n",
|
|
|
+ dst_addr->sa_family);
|
|
|
+ err = -EAFNOSUPPORT;
|
|
|
+ return (struct iscsi_endpoint *)ERR_PTR(err);
|
|
|
+ }
|
|
|
+
|
|
|
if (IS_ERR(csk))
|
|
|
return (struct iscsi_endpoint *)csk;
|
|
|
cxgbi_sock_get(csk);
|