@@ -20,6 +20,7 @@
#include <asm/unaligned.h>
#include <linux/inet.h>
#include <net/ipv6.h>
+#include <net/inet_hashtables.h>
#include <scsi/scsi_proto.h>
#include <scsi/iscsi_proto.h>
#include <scsi/scsi_tcq.h>
@@ -264,13 +265,21 @@ int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg,
bool iscsit_check_np_match(
struct sockaddr_storage *sockaddr,
struct iscsi_np *np,
- int network_transport)
+ int network_transport,
+ char *iface)
{
struct sockaddr_in *sock_in, *sock_in_e;
struct sockaddr_in6 *sock_in6, *sock_in6_e;
- bool ip_match = false;
+ bool ip_match = false, iface_match = false;
u16 port, port_e;
+ if (iface && np->iface) {
+ if (! strcmp(iface, np->iface))
+ iface_match = true;
+ } else if (!iface && !np->iface) {
+ iface_match = true;
+ }
+
if (sockaddr->ss_family == AF_INET6) {
sock_in6 = (struct sockaddr_in6 *)sockaddr;
sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr;
@@ -294,6 +303,7 @@ bool iscsit_check_np_match(
}
if (ip_match && (port_e == port) &&
+ iface_match &&
(np->np_network_transport == network_transport))
return true;
@@ -302,7 +312,8 @@ bool iscsit_check_np_match(
static struct iscsi_np *iscsit_get_np(
struct sockaddr_storage *sockaddr,
- int network_transport)
+ int network_transport,
+ char *iface)
{
struct iscsi_np *np;
bool match;
@@ -316,7 +327,7 @@ static struct iscsi_np *iscsit_get_np(
continue;
}
- match = iscsit_check_np_match(sockaddr, np, network_transport);
+ match = iscsit_check_np_match(sockaddr, np, network_transport, iface);
if (match) {
/*
* Increment the np_exports reference count now to
@@ -335,17 +346,18 @@ static struct iscsi_np *iscsit_get_np(
struct iscsi_np *iscsit_add_np(
struct sockaddr_storage *sockaddr,
- int network_transport)
+ int network_transport,
+ char *iface)
{
- struct iscsi_np *np;
- int ret;
+ struct iscsi_np *np;
+ int ret;
mutex_lock(&np_lock);
/*
* Locate the existing struct iscsi_np if already active..
*/
- np = iscsit_get_np(sockaddr, network_transport);
+ np = iscsit_get_np(sockaddr, network_transport, iface);
if (np) {
mutex_unlock(&np_lock);
return np;
@@ -359,6 +371,9 @@ struct iscsi_np *iscsit_add_np(
np->np_flags |= NPF_IP_NETWORK;
np->np_network_transport = network_transport;
+ if (iface) {
+ np->iface = kstrdup(iface, GFP_KERNEL);
+ }
spin_lock_init(&np->np_thread_lock);
init_completion(&np->np_restart_comp);
INIT_LIST_HEAD(&np->np_list);
@@ -468,6 +483,10 @@ int iscsit_del_np(struct iscsi_np *np)
&np->np_sockaddr, np->np_transport->name);
iscsit_put_transport(np->np_transport);
+ if (np->iface) {
+ kfree(np->iface);
+ np->iface = NULL;
+ }
kfree(np);
return 0;
}
@@ -1732,7 +1751,7 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
if (!cmd)
return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
(unsigned char *)hdr);
-
+
return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
(unsigned char *)hdr);
}
@@ -3343,6 +3362,68 @@ iscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
#define SENDTARGETS_BUF_LIMIT 32768U
+static inline bool match_port(struct sockaddr *addr, struct sock *sk);
+static inline bool match_port(struct sockaddr *addr, struct sock *sk)
+{
+ struct sockaddr_in *sock_in_e;
+ struct sockaddr_in6 *sock_in6_e;
+ u16 port, port_e;
+ __portpair ports;
+
+ port = sk->sk_dport;
+ if (sk->sk_family == AF_INET6) {
+ sock_in6_e = (struct sockaddr_in6 *)addr;
+ port_e = ntohs(sock_in6_e->sin6_port);
+ } else {
+ sock_in_e = (struct sockaddr_in *)addr;
+ port_e = ntohs(sock_in_e->sin_port);
+ }
+ ports = INET_COMBINED_PORTS(port, port_e);
+ return (sk->sk_portpair == ports);
+}
+
+static inline bool match_addr(struct sockaddr *addr, struct sock *sk);
+static inline bool match_addr(struct sockaddr *addr, struct sock *sk)
+{
+ struct sockaddr_in *sock_in_e;
+ struct sockaddr_in6 *sock_in6_e;
+
+ if (inet_addr_is_any(addr))
+ return true;
+ if (sk->sk_family == AF_INET6) {
+ sock_in6_e = (struct sockaddr_in6 *)addr;
+ return (!memcmp(&sk->sk_v6_rcv_saddr,
+ &sock_in6_e->sin6_addr,
+ sizeof(struct in6_addr)));
+ }
+ sock_in_e = (struct sockaddr_in *)addr;
+ return (sk->sk_rcv_saddr == sock_in_e->sin_addr.s_addr);
+}
+
+static bool match_conn(struct iscsi_np *np, struct iscsi_conn *conn);
+static bool match_conn(struct iscsi_np *np, struct iscsi_conn *conn)
+{
+ /*
+ * when the target listen on ::0 it also replies for 0.0.0.0
+ * so avoid discarding the packet if it the ss_family does not
+ * match if the ip we are bound to is one of those.
+ */
+ if (conn->sock->sk->sk_family != np->np_sockaddr.ss_family
+ && !inet_addr_is_any((struct sockaddr *)&np->np_sockaddr)) {
+ return false;
+ }
+ if (np->iface != NULL && np->ifindex != conn->sock->sk->sk_bound_dev_if) {
+ return false;
+ }
+ if (!match_port((struct sockaddr *)&np->np_sockaddr, conn->sock->sk)) {
+ return false;
+ }
+ if (!match_addr((struct sockaddr *)&np->np_sockaddr, conn->sock->sk)) {
+ return false;
+ }
+ return true;
+}
+
static int
iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
enum iscsit_transport_type network_transport,
@@ -3421,6 +3502,9 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
struct iscsi_np *np = tpg_np->tpg_np;
struct sockaddr_storage *sockaddr;
+ if (match_conn(np, conn) == false)
+ continue;
+
if (np->np_network_transport != network_transport)
continue;
@@ -24,9 +24,11 @@ extern void iscsit_login_kref_put(struct kref *);
extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *,
struct iscsi_tpg_np *);
extern bool iscsit_check_np_match(struct sockaddr_storage *,
- struct iscsi_np *, int);
+ struct iscsi_np *, int network_transport,
+ char *iface);
extern struct iscsi_np *iscsit_add_np(struct sockaddr_storage *,
- int);
+ int network_transport,
+ char *iface);
extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
struct iscsi_portal_group *, bool);
extern int iscsit_del_np(struct iscsi_np *);
@@ -92,7 +92,7 @@ static ssize_t lio_target_np_driver_store(struct config_item *item,
}
tpg_np_new = iscsit_tpg_add_network_portal(tpg,
- &np->np_sockaddr, tpg_np, type);
+ &np->np_sockaddr, tpg_np, type, np->iface);
if (IS_ERR(tpg_np_new)) {
rc = PTR_ERR(tpg_np_new);
goto out;
@@ -158,7 +158,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
{
struct iscsi_portal_group *tpg;
struct iscsi_tpg_np *tpg_np;
- char *str, *str2, *ip_str, *port_str;
+ char *str, *str2, *ip_str, *port_str, *iface_str;
struct sockaddr_storage sockaddr = { };
int ret;
char buf[MAX_PORTAL_LEN + 1] = { };
@@ -202,11 +202,18 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
*port_str = '\0'; /* Terminate string for IP */
port_str++; /* Skip over ":" */
}
-
+ /* now lets look if there is one more ':' with an interface after */
+ iface_str = strstr(port_str, ":");
+ if (iface_str) {
+ /* It is found */
+ *iface_str = '\0'; /* Terminate string for port */
+ iface_str++;
+ }
ret = inet_pton_with_scope(&init_net, AF_UNSPEC, ip_str,
port_str, &sockaddr);
if (ret) {
- pr_err("malformed ip/port passed: %s\n", name);
+ pr_err("malformed ip/port/interface passed: %s/%s/%s\n",
+ ip_str, port_str, iface_str);
return ERR_PTR(ret);
}
@@ -233,7 +240,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
*
*/
tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, NULL,
- ISCSI_TCP);
+ ISCSI_TCP, iface_str);
if (IS_ERR(tpg_np)) {
iscsit_put_tpg(tpg);
return ERR_CAST(tpg_np);
@@ -17,6 +17,7 @@
#include <linux/tcp.h> /* TCP_NODELAY */
#include <net/ip.h>
#include <net/ipv6.h> /* ipv6_addr_v4mapped() */
+#include <net/sock.h> /* sock_bindtoindex() */
#include <scsi/iscsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
@@ -856,6 +857,8 @@ int iscsit_setup_np(
struct sockaddr_storage *sockaddr)
{
struct socket *sock = NULL;
+ struct net *net = NULL;
+ struct net_device *dev = NULL;
int backlog = ISCSIT_TCP_BACKLOG, ret, len;
switch (np->np_network_transport) {
@@ -902,6 +905,24 @@ int iscsit_setup_np(
tcp_sock_set_nodelay(sock->sk);
sock_set_reuseaddr(sock->sk);
ip_sock_set_freebind(sock->sk);
+ if (np->iface) {
+ net = sock_net(sock->sk);
+ rcu_read_lock();
+ dev = dev_get_by_name_rcu(net, np->iface);
+ if (dev) {
+ np->ifindex = dev->ifindex;
+ rcu_read_unlock();
+ ret = sock_bindtoindex(sock->sk, np->ifindex, true);
+ if (ret < 0) {
+ pr_err("sock_bindtoindex() failed: %d\n", ret);
+ goto fail;
+ }
+ } else {
+ rcu_read_unlock();
+ pr_err("dev_get_by_name_rcu() failed\n");
+ goto fail;
+ }
+ }
ret = kernel_bind(sock, (struct sockaddr *)&np->np_sockaddr, len);
if (ret < 0) {
@@ -423,7 +423,8 @@ struct iscsi_tpg_np *iscsit_tpg_locate_child_np(
static bool iscsit_tpg_check_network_portal(
struct iscsi_tiqn *tiqn,
struct sockaddr_storage *sockaddr,
- int network_transport)
+ int network_transport,
+ char *iface)
{
struct iscsi_portal_group *tpg;
struct iscsi_tpg_np *tpg_np;
@@ -438,7 +439,7 @@ static bool iscsit_tpg_check_network_portal(
np = tpg_np->tpg_np;
match = iscsit_check_np_match(sockaddr, np,
- network_transport);
+ network_transport, iface);
if (match)
break;
}
@@ -456,14 +457,15 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
struct iscsi_portal_group *tpg,
struct sockaddr_storage *sockaddr,
struct iscsi_tpg_np *tpg_np_parent,
- int network_transport)
+ int network_transport,
+ char *iface)
{
struct iscsi_np *np;
struct iscsi_tpg_np *tpg_np;
if (!tpg_np_parent) {
if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr,
- network_transport)) {
+ network_transport, iface)) {
pr_err("Network Portal: %pISc already exists on a"
" different TPG on %s\n", sockaddr,
tpg->tpg_tiqn->tiqn);
@@ -478,7 +480,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
return ERR_PTR(-ENOMEM);
}
- np = iscsit_add_np(sockaddr, network_transport);
+ np = iscsit_add_np(sockaddr, network_transport, iface);
if (IS_ERR(np)) {
kfree(tpg_np);
return ERR_CAST(np);
@@ -33,7 +33,7 @@ extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *);
extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int);
extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *,
struct sockaddr_storage *, struct iscsi_tpg_np *,
- int);
+ int network_transport, char *iface);
extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *,
struct iscsi_tpg_np *);
extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32);
@@ -795,6 +795,8 @@ struct iscsi_np {
void *np_context;
struct iscsit_transport *np_transport;
struct list_head np_list;
+ char *iface;
+ int ifindex;
} ____cacheline_aligned;
struct iscsi_tpg_np {