#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>
 }
 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;
                "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)
 {
        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",
                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;
        }
 
                        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);
 {
        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;
                }
 
        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;
        }
        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);
 
        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)
 {
 }
 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)
 {
                }
        }
 
-       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);