linux/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
   2/*
   3 * Copyright 2021 Marvell. All rights reserved.
   4 */
   5
   6#include <linux/types.h>
   7#include <asm/byteorder.h>
   8#include <asm/param.h>
   9#include <linux/delay.h>
  10#include <linux/pci.h>
  11#include <linux/dma-mapping.h>
  12#include <linux/etherdevice.h>
  13#include <linux/kernel.h>
  14#include <linux/stddef.h>
  15#include <linux/errno.h>
  16
  17#include <net/tcp.h>
  18
  19#include <linux/qed/qed_nvmetcp_ip_services_if.h>
  20
  21#define QED_IP_RESOL_TIMEOUT  4
  22
  23int qed_route_ipv4(struct sockaddr_storage *local_addr,
  24                   struct sockaddr_storage *remote_addr,
  25                   struct sockaddr *hardware_address,
  26                   struct net_device **ndev)
  27{
  28        struct neighbour *neigh = NULL;
  29        __be32 *loc_ip, *rem_ip;
  30        struct rtable *rt;
  31        int rc = -ENXIO;
  32        int retry;
  33
  34        loc_ip = &((struct sockaddr_in *)local_addr)->sin_addr.s_addr;
  35        rem_ip = &((struct sockaddr_in *)remote_addr)->sin_addr.s_addr;
  36        *ndev = NULL;
  37        rt = ip_route_output(&init_net, *rem_ip, *loc_ip, 0/*tos*/, 0/*oif*/);
  38        if (IS_ERR(rt)) {
  39                pr_err("lookup route failed\n");
  40                rc = PTR_ERR(rt);
  41                goto return_err;
  42        }
  43
  44        neigh = dst_neigh_lookup(&rt->dst, rem_ip);
  45        if (!neigh) {
  46                rc = -ENOMEM;
  47                ip_rt_put(rt);
  48                goto return_err;
  49        }
  50
  51        *ndev = rt->dst.dev;
  52        ip_rt_put(rt);
  53
  54        /* If not resolved, kick-off state machine towards resolution */
  55        if (!(neigh->nud_state & NUD_VALID))
  56                neigh_event_send(neigh, NULL);
  57
  58        /* query neighbor until resolved or timeout */
  59        retry = QED_IP_RESOL_TIMEOUT;
  60        while (!(neigh->nud_state & NUD_VALID) && retry > 0) {
  61                msleep(1000);
  62                retry--;
  63        }
  64
  65        if (neigh->nud_state & NUD_VALID) {
  66                /* copy resolved MAC address */
  67                neigh_ha_snapshot(hardware_address->sa_data, neigh, *ndev);
  68                hardware_address->sa_family = (*ndev)->type;
  69                rc = 0;
  70        }
  71
  72        neigh_release(neigh);
  73        if (!(*loc_ip)) {
  74                *loc_ip = inet_select_addr(*ndev, *rem_ip, RT_SCOPE_UNIVERSE);
  75                local_addr->ss_family = AF_INET;
  76        }
  77
  78return_err:
  79
  80        return rc;
  81}
  82EXPORT_SYMBOL(qed_route_ipv4);
  83
  84int qed_route_ipv6(struct sockaddr_storage *local_addr,
  85                   struct sockaddr_storage *remote_addr,
  86                   struct sockaddr *hardware_address,
  87                   struct net_device **ndev)
  88{
  89        struct neighbour *neigh = NULL;
  90        struct dst_entry *dst;
  91        struct flowi6 fl6;
  92        int rc = -ENXIO;
  93        int retry;
  94
  95        memset(&fl6, 0, sizeof(fl6));
  96        fl6.saddr = ((struct sockaddr_in6 *)local_addr)->sin6_addr;
  97        fl6.daddr = ((struct sockaddr_in6 *)remote_addr)->sin6_addr;
  98        dst = ip6_route_output(&init_net, NULL, &fl6);
  99        if (!dst || dst->error) {
 100                if (dst) {
 101                        dst_release(dst);
 102                        pr_err("lookup route failed %d\n", dst->error);
 103                }
 104
 105                goto out;
 106        }
 107
 108        neigh = dst_neigh_lookup(dst, &fl6.daddr);
 109        if (neigh) {
 110                *ndev = ip6_dst_idev(dst)->dev;
 111
 112                /* If not resolved, kick-off state machine towards resolution */
 113                if (!(neigh->nud_state & NUD_VALID))
 114                        neigh_event_send(neigh, NULL);
 115
 116                /* query neighbor until resolved or timeout */
 117                retry = QED_IP_RESOL_TIMEOUT;
 118                while (!(neigh->nud_state & NUD_VALID) && retry > 0) {
 119                        msleep(1000);
 120                        retry--;
 121                }
 122
 123                if (neigh->nud_state & NUD_VALID) {
 124                        neigh_ha_snapshot((u8 *)hardware_address->sa_data,
 125                                          neigh, *ndev);
 126                        hardware_address->sa_family = (*ndev)->type;
 127                        rc = 0;
 128                }
 129
 130                neigh_release(neigh);
 131
 132                if (ipv6_addr_any(&fl6.saddr)) {
 133                        if (ipv6_dev_get_saddr(dev_net(*ndev), *ndev,
 134                                               &fl6.daddr, 0, &fl6.saddr)) {
 135                                pr_err("Unable to find source IP address\n");
 136                                goto out;
 137                        }
 138
 139                        local_addr->ss_family = AF_INET6;
 140                        ((struct sockaddr_in6 *)local_addr)->sin6_addr =
 141                                                                fl6.saddr;
 142                }
 143        }
 144
 145        dst_release(dst);
 146
 147out:
 148
 149        return rc;
 150}
 151EXPORT_SYMBOL(qed_route_ipv6);
 152
 153void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id)
 154{
 155        if (is_vlan_dev(*ndev)) {
 156                *vlan_id = vlan_dev_vlan_id(*ndev);
 157                *ndev = vlan_dev_real_dev(*ndev);
 158        }
 159}
 160EXPORT_SYMBOL(qed_vlan_get_ndev);
 161
 162struct pci_dev *qed_validate_ndev(struct net_device *ndev)
 163{
 164        struct pci_dev *pdev = NULL;
 165        struct net_device *upper;
 166
 167        for_each_pci_dev(pdev) {
 168                if (pdev && pdev->driver &&
 169                    !strcmp(pdev->driver->name, "qede")) {
 170                        upper = pci_get_drvdata(pdev);
 171                        if (upper->ifindex == ndev->ifindex)
 172                                return pdev;
 173                }
 174        }
 175
 176        return NULL;
 177}
 178EXPORT_SYMBOL(qed_validate_ndev);
 179
 180__be16 qed_get_in_port(struct sockaddr_storage *sa)
 181{
 182        return sa->ss_family == AF_INET
 183                ? ((struct sockaddr_in *)sa)->sin_port
 184                : ((struct sockaddr_in6 *)sa)->sin6_port;
 185}
 186EXPORT_SYMBOL(qed_get_in_port);
 187
 188int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr,
 189                       struct socket **sock, u16 *port)
 190{
 191        struct sockaddr_storage sa;
 192        int rc = 0;
 193
 194        rc = sock_create(local_ip_addr.ss_family, SOCK_STREAM, IPPROTO_TCP,
 195                         sock);
 196        if (rc) {
 197                pr_warn("failed to create socket: %d\n", rc);
 198                goto err;
 199        }
 200
 201        (*sock)->sk->sk_allocation = GFP_KERNEL;
 202        sk_set_memalloc((*sock)->sk);
 203
 204        rc = kernel_bind(*sock, (struct sockaddr *)&local_ip_addr,
 205                         sizeof(local_ip_addr));
 206
 207        if (rc) {
 208                pr_warn("failed to bind socket: %d\n", rc);
 209                goto err_sock;
 210        }
 211
 212        rc = kernel_getsockname(*sock, (struct sockaddr *)&sa);
 213        if (rc < 0) {
 214                pr_warn("getsockname() failed: %d\n", rc);
 215                goto err_sock;
 216        }
 217
 218        *port = ntohs(qed_get_in_port(&sa));
 219
 220        return 0;
 221
 222err_sock:
 223        sock_release(*sock);
 224        sock = NULL;
 225err:
 226
 227        return rc;
 228}
 229EXPORT_SYMBOL(qed_fetch_tcp_port);
 230
 231void qed_return_tcp_port(struct socket *sock)
 232{
 233        if (sock && sock->sk) {
 234                tcp_set_state(sock->sk, TCP_CLOSE);
 235                sock_release(sock);
 236        }
 237}
 238EXPORT_SYMBOL(qed_return_tcp_port);
 239