/* * iSCSI iface helpers * * Copyright (C) 2008 Mike Christie * Copyright (C) 2008 Red Hat, Inc. All rights reserved. * maintained by open-iscsi@@googlegroups.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * See the file COPYING included with this distribution for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "list.h" #include "iscsi_sysfs.h" #include "iscsi_settings.h" #include "config.h" #include "transport.h" #include "idbm.h" #include "iface.h" #include "session_info.h" #include "host.h" #include "fw_context.h" #include "sysdeps.h" #include "iscsi_err.h" #include "iscsi_netlink.h" #define _unwrap(x) (x && strlen(x)) ? x : UNKNOWN_VALUE /* * Default ifaces for use with transports that do not bind to hardware * by defaults (transports that let the interconnect layer to the routing * by defaults). */ /* * iSCSI over TCP/IP */ static struct iface_rec iface_default = { .name = "default", .transport_name = "tcp", }; /* * iSER */ static struct iface_rec iface_iser = { .name = "iser", .transport_name = "iser", }; static struct iface_rec *default_ifaces[] = { &iface_default, &iface_iser, NULL, }; static struct iface_rec *iface_match_default(struct iface_rec *iface) { struct iface_rec *def_iface; int i = 0; while ((def_iface = default_ifaces[i++])) { if (!strcmp(iface->name, def_iface->name)) return def_iface; } return NULL; } static void iface_init(struct iface_rec *iface) { if (!strlen(iface->name)) sprintf(iface->name, DEFAULT_IFACENAME); } /* * Default is to use tcp through whatever the network layer * selects for us with the initiatorname.iscsi iname. */ void iface_setup_defaults(struct iface_rec *iface) { sprintf(iface->transport_name, DEFAULT_TRANSPORT); iface_init(iface); } struct iface_rec *iface_alloc(char *ifname, int *err) { struct iface_rec *iface; if (!strlen(ifname) || strlen(ifname) + 1 > ISCSI_MAX_IFACE_LEN) { *err = ISCSI_ERR_INVAL; return NULL; } iface = calloc(1, sizeof(*iface)); if (!iface) { *err = ISCSI_ERR_NOMEM; return NULL; } strlcpy(iface->name, ifname, ISCSI_MAX_IFACE_LEN); INIT_LIST_HEAD(&iface->list); return iface; } static int __iface_conf_read(struct iface_rec *iface) { char *iface_conf; recinfo_t *info; FILE *f; int rc = 0; iface_conf = calloc(1, PATH_MAX); if (!iface_conf) return ISCSI_ERR_NOMEM; info = idbm_recinfo_alloc(MAX_KEYS); if (!info) { rc = ISCSI_ERR_NOMEM; goto free_conf; } snprintf(iface_conf, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface->name); log_debug(5, "looking for iface conf %s", iface_conf); f = fopen(iface_conf, "r"); if (!f) { /* * if someone passes in default but has not defined * an iface with default then we do it for them */ if (!strcmp(iface->name, DEFAULT_IFACENAME)) { iface_setup_defaults(iface); rc = 0; } else rc = ISCSI_ERR_IDBM; goto free_info; } iface_init(iface); idbm_recinfo_iface(iface, info); idbm_recinfo_config(info, f); fclose(f); free_info: free(info); free_conf: free(iface_conf); return rc; } int iface_conf_read(struct iface_rec *iface) { struct iface_rec *def_iface; int rc, retry = 0; def_iface = iface_match_default(iface); if (def_iface) { /* * older tools allowed default to have different * transport_names so we do not want to overwrite * it. */ if (!strcmp(def_iface->name, DEFAULT_IFACENAME)) { if (!strlen(iface->name)) strcpy(iface->name, def_iface->name); if (!strlen(iface->netdev)) strcpy(iface->netdev, def_iface->netdev); if (!strlen(iface->hwaddress)) strcpy(iface->hwaddress, def_iface->hwaddress); if (!strlen(iface->transport_name)) strcpy(iface->transport_name, def_iface->transport_name); if (!strlen(iface->iname)) strcpy(iface->iname, def_iface->iname); } else { iface_init(iface); iface_copy(iface, def_iface); } return 0; } retry_read: rc = idbm_lock(); if (rc) return rc; rc = __iface_conf_read(iface); idbm_unlock(); /* * cmd was run before running -m iface, so force def bindings * creation to see if that was the one requested */ if (retry < 1 && rc == ISCSI_ERR_IDBM) { iface_setup_host_bindings(); retry++; goto retry_read; } return rc; } int iface_conf_delete(struct iface_rec *iface) { struct iface_rec *def_iface; char *iface_conf; int rc = 0; def_iface = iface_match_default(iface); if (def_iface) { log_error("iface %s is a special interface and " "cannot be deleted.", iface->name); return ISCSI_ERR_INVAL; } iface_conf = calloc(1, PATH_MAX); if (!iface_conf) return ISCSI_ERR_NOMEM; sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name); rc = idbm_lock(); if (rc) goto free_conf; if (unlink(iface_conf)) rc = ISCSI_ERR_IDBM; idbm_unlock(); free_conf: free(iface_conf); return rc; } int iface_conf_write(struct iface_rec *iface) { struct iface_rec *def_iface; char *iface_conf; FILE *f; int rc = 0; def_iface = iface_match_default(iface); if (def_iface) { log_error("iface %s is a special interface and " "is not stored in %s.", iface->name, IFACE_CONFIG_DIR); return ISCSI_ERR_INVAL; } iface_conf = calloc(1, PATH_MAX); if (!iface_conf) return ISCSI_ERR_NOMEM; sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name); f = fopen(iface_conf, "w"); if (!f) { rc = ISCSI_ERR_IDBM; goto free_conf; } rc = idbm_lock(); if (rc) goto close_f; idbm_print(IDBM_PRINT_TYPE_IFACE, iface, 1, f); idbm_unlock(); close_f: fclose(f); free_conf: free(iface_conf); return rc; } int iface_conf_update(struct list_head *params, struct iface_rec *iface) { struct iface_rec *def_iface; recinfo_t *info; struct user_param *param; int rc = 0; def_iface = iface_match_default(iface); if (def_iface) { log_error("iface %s is a special interface and " "cannot be modified.", iface->name); return ISCSI_ERR_INVAL; } info = idbm_recinfo_alloc(MAX_KEYS); if (!info) return ISCSI_ERR_NOMEM; idbm_recinfo_iface(iface, info); list_for_each_entry(param, params, list) { rc = idbm_verify_param(info, param->name); if (rc) goto free_info; } list_for_each_entry(param, params, list) { rc = idbm_rec_update_param(info, param->name, param->value, 0); if (rc) goto free_info; } rc = iface_conf_write(iface); free_info: free(info); return rc; } #if 0 /* Unused */ static int iface_get_next_id(void) { struct stat statb; char *iface_conf; int i, rc = ENOSPC; iface_conf = calloc(1, PATH_MAX); if (!iface_conf) return ENOMEM; for (i = 0; i < INT_MAX; i++) { memset(iface_conf, 0, PATH_MAX); /* check len */ snprintf(iface_conf, PATH_MAX, "iface%d", i); if (strlen(iface_conf) > ISCSI_MAX_IFACE_LEN - 1) { log_error("iface namespace is full. Remove unused " "iface definitions from %s or send mail " "to open-iscsi@googlegroups.com to report " "the problem", IFACE_CONFIG_DIR); rc = ENOSPC; break; } memset(iface_conf, 0, PATH_MAX); snprintf(iface_conf, PATH_MAX, "%s/iface%d", IFACE_CONFIG_DIR, i); if (!stat(iface_conf, &statb)) continue; if (errno == ENOENT) { rc = i; break; } } free(iface_conf); return rc; } #endif /* Unused */ struct iface_search { struct iface_rec *pattern; struct iface_rec *found; }; static int __iface_get_by_net_binding(void *data, struct iface_rec *iface) { struct iface_search *search = data; if (!strcmp(search->pattern->name, iface->name)) { iface_copy(search->found, iface); return 1; } if (iface_is_bound_by_hwaddr(search->pattern)) { if (!strcasecmp(iface->hwaddress, search->pattern->hwaddress)) { iface_copy(search->found, iface); return 1; } else return 0; } if (iface_is_bound_by_netdev(search->pattern)) { if (!strcmp(iface->netdev, search->pattern->netdev)) { iface_copy(search->found, iface); return 1; } else return 0; } /* if (iface_is_bound_by_ipaddr(search->pattern)) { if (!strcmp(iface->ipaddress, search->pattern->ipaddress)) { iface_copy(search->found, iface); return 1; } else return 0; } */ return 0; } /* * Before 2.0.870, we only could bind by netdeivce or hwaddress, * so we did a simple reverse lookup to go from sysfs info to * the iface name. After 2.0.870 we added a lot of options to the * iface binding so we added the ifacename to the kernel. * * This function is for older kernels that do not export the ifacename. * If the user was doing iscsi_tcp session binding we will find * the iface by matching net info. */ int iface_get_by_net_binding(struct iface_rec *pattern, struct iface_rec *out_rec) { int num_found = 0, rc; struct iface_search search; if (!iface_is_bound_by_hwaddr(pattern) && !iface_is_bound_by_netdev(pattern)) { sprintf(out_rec->name, DEFAULT_IFACENAME); return 0; } search.pattern = pattern; search.found = out_rec; rc = iface_for_each_iface(&search, 0, &num_found, __iface_get_by_net_binding); if (rc == 1) return 0; return ISCSI_ERR_NO_OBJS_FOUND; } /* * detect IPv4 vs IPv4 IP address */ enum iscsi_iface_type iface_get_iptype(struct iface_rec *iface) { enum iscsi_iface_type res = ISCSI_IFACE_TYPE_IPV4; /* address might not be set if user config with another tool */ if (!strlen(iface->ipaddress) || !strcmp(UNKNOWN_VALUE, iface->ipaddress)) { /* unknown or empty IP address: try to figure out by name */ if (strstr(iface->name, "ipv6")) res = ISCSI_IFACE_TYPE_IPV6; } else { /* figure out what type of IP address string we have */ if (strcmp(iface->bootproto, "dhcp") && !strchr(iface->ipaddress, '.')) { /* bootproto is NOT "dhcp", IP addr does NOT have a dot in it */ res = ISCSI_IFACE_TYPE_IPV6; } } log_debug(8, "iface: ipaddr=\"%s\" name=\"%s\" bootproto=\"%s\" -> %s", iface->ipaddress, iface->name, iface->bootproto, res == ISCSI_IFACE_TYPE_IPV4 ? "IPv4" : "IPv6"); return res; } static int iface_setup_binding_from_kern_iface(void *data, struct iface_rec *kern_iface) { struct host_info *hinfo = data; struct iface_rec iface; char iface_path[PATH_MAX]; if (!strlen(hinfo->iface.hwaddress)) { log_error("Invalid offload iSCSI host %u. Missing " "hwaddress. Try upgrading %s driver.", hinfo->host_no, hinfo->iface.transport_name); return 0; } memset(&iface, 0, sizeof(struct iface_rec)); if (kern_iface) { memcpy(&iface, kern_iface, sizeof(iface)); snprintf(iface.name, sizeof(iface.name), "%s.%s.%s.%u", kern_iface->transport_name, kern_iface->hwaddress, iface_get_iptype(kern_iface) == ISCSI_IFACE_TYPE_IPV4 ? "ipv4" : "ipv6", kern_iface->iface_num); } else { snprintf(iface.name, sizeof(iface.name), "%s.%s", hinfo->iface.transport_name, hinfo->iface.hwaddress); } strcpy(iface.hwaddress, hinfo->iface.hwaddress); strcpy(iface.transport_name, hinfo->iface.transport_name); memset(iface_path, 0, sizeof(iface_path)); snprintf(iface_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface.name); if (access(iface_path, F_OK) != 0) { /* not found so create it */ if (iface_conf_write(&iface)) { log_error("Could not create default iface conf %s.", iface.name); /* fall through - will not be persistent */ } } return 0; } static int __iface_setup_host_bindings(__attribute__((unused))void *data, struct host_info *hinfo) { struct iface_rec *def_iface; struct iscsi_transport *t; int i = 0, nr_found; t = iscsi_sysfs_get_transport_by_hba(hinfo->host_no); if (!t) return 0; /* do not setup binding for hosts using non offload drivers */ while ((def_iface = default_ifaces[i++])) { if (!strcmp(t->name, def_iface->transport_name)) return 0; } nr_found = 0; iscsi_sysfs_for_each_iface_on_host(hinfo, hinfo->host_no, &nr_found, iface_setup_binding_from_kern_iface); if (!nr_found) iface_setup_binding_from_kern_iface(hinfo, NULL); return 0; } /* * Create a default iface for offload cards. We assume that we will * be able identify each host by MAC. */ void iface_setup_host_bindings(void) { int nr_found = 0; if (idbm_lock()) return; if (access(IFACE_CONFIG_DIR, F_OK) != 0) { if (mkdir(IFACE_CONFIG_DIR, 0770) != 0) { log_error("Could not make %s. HW/OFFLOAD iscsi " "may not be supported", IFACE_CONFIG_DIR); idbm_unlock(); return; } } idbm_unlock(); transport_probe_for_offload(); if (iscsi_sysfs_for_each_host(NULL, &nr_found, __iface_setup_host_bindings)) log_error("Could not scan scsi hosts. HW/OFFLOAD iscsi " "operations may not be supported, or please " "see README for instructions on setting up ifaces."); } void iface_copy(struct iface_rec *dst, struct iface_rec *src) { if (strlen(src->name)) strcpy(dst->name, src->name); if (src->iface_num) dst->iface_num = src->iface_num; if (strlen(src->netdev)) strcpy(dst->netdev, src->netdev); if (strlen(src->ipaddress)) strcpy(dst->ipaddress, src->ipaddress); if (strlen(src->subnet_mask)) strcpy(dst->subnet_mask, src->subnet_mask); if (strlen(src->gateway)) strcpy(dst->gateway, src->gateway); if (strlen(src->bootproto)) strcpy(dst->bootproto, src->bootproto); if (strlen(src->ipv6_linklocal)) strcpy(dst->ipv6_linklocal, src->ipv6_linklocal); if (strlen(src->ipv6_router)) strcpy(dst->ipv6_router, src->ipv6_router); if (strlen(src->ipv6_autocfg)) strcpy(dst->ipv6_autocfg, src->ipv6_autocfg); if (strlen(src->linklocal_autocfg)) strcpy(dst->linklocal_autocfg, src->linklocal_autocfg); if (strlen(src->router_autocfg)) strcpy(dst->router_autocfg, src->router_autocfg); if (src->vlan_id) dst->vlan_id = src->vlan_id; if (src->vlan_priority) dst->vlan_priority = src->vlan_priority; if (strlen(src->vlan_state)) strcpy(dst->vlan_state, src->vlan_state); if (strlen(src->state)) strcpy(dst->state, src->state); if (src->mtu) dst->mtu = src->mtu; if (src->port) dst->port = src->port; if (strlen(src->delayed_ack)) strcpy(dst->delayed_ack, src->delayed_ack); if (strlen(src->nagle)) strcpy(dst->nagle, src->nagle); if (strlen(src->tcp_wsf_state)) strcpy(dst->tcp_wsf_state, src->tcp_wsf_state); if (src->tcp_wsf) dst->tcp_wsf = src->tcp_wsf; if (src->tcp_timer_scale) dst->tcp_timer_scale = src->tcp_timer_scale; if (strlen(src->tcp_timestamp)) strcpy(dst->tcp_timestamp, src->tcp_timestamp); if (strlen(src->dhcp_dns)) strcpy(dst->dhcp_dns, src->dhcp_dns); if (strlen(src->dhcp_slp_da)) strcpy(dst->dhcp_slp_da, src->dhcp_slp_da); if (strlen(src->tos_state)) strcpy(dst->tos_state, src->tos_state); if (src->tos) dst->tos = src->tos; if (strlen(src->gratuitous_arp)) strcpy(dst->gratuitous_arp, src->gratuitous_arp); if (strlen(src->dhcp_alt_client_id_state)) strcpy(dst->dhcp_alt_client_id_state, src->dhcp_alt_client_id_state); if (strlen(src->dhcp_alt_client_id)) strcpy(dst->dhcp_alt_client_id, src->dhcp_alt_client_id); if (strlen(src->dhcp_req_vendor_id_state)) strcpy(dst->dhcp_req_vendor_id_state, src->dhcp_req_vendor_id_state); if (strlen(src->dhcp_vendor_id_state)) strcpy(dst->dhcp_vendor_id_state, src->dhcp_vendor_id_state); if (strlen(src->dhcp_vendor_id)) strcpy(dst->dhcp_vendor_id, src->dhcp_vendor_id); if (strlen(src->dhcp_learn_iqn)) strcpy(dst->dhcp_learn_iqn, src->dhcp_learn_iqn); if (strlen(src->fragmentation)) strcpy(dst->fragmentation, src->fragmentation); if (strlen(src->incoming_forwarding)) strcpy(dst->incoming_forwarding, src->incoming_forwarding); if (src->ttl) dst->ttl = src->ttl; if (strlen(src->gratuitous_neighbor_adv)) strcpy(dst->gratuitous_neighbor_adv, src->gratuitous_neighbor_adv); if (strlen(src->redirect)) strcpy(dst->redirect, src->redirect); if (strlen(src->mld)) strcpy(dst->mld, src->mld); if (src->flow_label) dst->flow_label = src->flow_label; if (src->traffic_class) dst->traffic_class = src->traffic_class; if (src->hop_limit) dst->hop_limit = src->hop_limit; if (src->nd_reachable_tmo) dst->nd_reachable_tmo = src->nd_reachable_tmo; if (src->nd_rexmit_time) dst->nd_rexmit_time = src->nd_rexmit_time; if (src->nd_stale_tmo) dst->nd_stale_tmo = src->nd_stale_tmo; if (src->dup_addr_detect_cnt) dst->dup_addr_detect_cnt = src->dup_addr_detect_cnt; if (src->router_adv_link_mtu) dst->router_adv_link_mtu = src->router_adv_link_mtu; if (src->def_task_mgmt_tmo) dst->def_task_mgmt_tmo = src->def_task_mgmt_tmo; if (strlen(src->header_digest)) strcpy(dst->header_digest, src->header_digest); if (strlen(src->data_digest)) strcpy(dst->data_digest, src->data_digest); if (strlen(src->immediate_data)) strcpy(dst->immediate_data, src->immediate_data); if (strlen(src->initial_r2t)) strcpy(dst->initial_r2t, src->initial_r2t); if (strlen(src->data_seq_inorder)) strcpy(dst->data_seq_inorder, src->data_seq_inorder); if (strlen(src->data_pdu_inorder)) strcpy(dst->data_pdu_inorder, src->data_pdu_inorder); if (src->erl) dst->erl = src->erl; if (src->max_recv_dlength) dst->max_recv_dlength = src->max_recv_dlength; if (src->first_burst_len) dst->first_burst_len = src->first_burst_len; if (src->max_out_r2t) dst->max_out_r2t = src->max_out_r2t; if (src->max_burst_len) dst->max_burst_len = src->max_burst_len; if (strlen(src->chap_auth)) strcpy(dst->chap_auth, src->chap_auth); if (strlen(src->bidi_chap)) strcpy(dst->bidi_chap, src->bidi_chap); if (strlen(src->strict_login_comp)) strcpy(dst->strict_login_comp, src->strict_login_comp); if (strlen(src->discovery_auth)) strcpy(dst->discovery_auth, src->discovery_auth); if (strlen(src->discovery_logout)) strcpy(dst->discovery_logout, src->discovery_logout); if (strlen(src->hwaddress)) strcpy(dst->hwaddress, src->hwaddress); if (strlen(src->transport_name)) strcpy(dst->transport_name, src->transport_name); if (strlen(src->iname)) strcpy(dst->iname, src->iname); } int iface_is_valid(struct iface_rec *iface) { if (!iface) return 0; if (!strlen(iface->name)) return 0; if (!strlen(iface->transport_name)) return 0; if (iface_is_bound_by_hwaddr(iface)) return 1; if (iface_is_bound_by_netdev(iface)) return 1; // if (iface_is_bound_by_ipaddr(iface)) // return 1; /* bound by transport name */ return 1; } int iface_match(struct iface_rec *pattern, struct iface_rec *iface) { if (!pattern || !iface) return 1; if (!strlen(pattern->name)) return 1; if (!strcmp(pattern->name, iface->name)) { if (!strcmp(pattern->name, DEFAULT_IFACENAME)) return 1; /* * For default we allow the same name, but different * transports. */ if (!strlen(pattern->transport_name)) return 1; if (!strcmp(pattern->transport_name, iface->transport_name)) return 1; /* fall through */ } return 0; } int iface_is_bound_by_hwaddr(struct iface_rec *iface) { if (iface && strlen(iface->hwaddress) && strcmp(iface->hwaddress, DEFAULT_HWADDRESS)) return 1; return 0; } int iface_is_bound_by_netdev(struct iface_rec *iface) { if (iface && strlen(iface->netdev) && strcmp(iface->netdev, DEFAULT_NETDEV)) return 1; return 0; } int iface_is_bound_by_ipaddr(struct iface_rec *iface) { if (iface && strlen(iface->ipaddress) && strcmp(iface->ipaddress, DEFAULT_IPADDRESS)) return 1; return 0; } void iface_print(struct iscsi_iface *iface, char *prefix) { const char *ipaddress = iscsi_iface_ipaddress_get(iface); printf("%sIface Name: %s\n", prefix, (strlen(iscsi_iface_name_get(iface)) > 0) ? iscsi_iface_name_get(iface) : UNKNOWN_VALUE); printf("%sIface Transport: %s\n", prefix, (strlen(iscsi_iface_transport_name_get(iface)) > 0) ? iscsi_iface_transport_name_get(iface) : UNKNOWN_VALUE); printf("%sIface Initiatorname: %s\n", prefix, (strlen(iscsi_iface_iname_get(iface)) > 0) ? iscsi_iface_iname_get(iface) : UNKNOWN_VALUE); if (!strlen(ipaddress)) printf("%sIface IPaddress: %s\n", prefix, UNKNOWN_VALUE); else if (strchr(ipaddress, '.')) printf("%sIface IPaddress: %s\n", prefix, ipaddress); else printf("%sIface IPaddress: [%s]\n", prefix, ipaddress); printf("%sIface HWaddress: %s\n", prefix, (strlen(iscsi_iface_hwaddress_get(iface)) > 0) ? iscsi_iface_hwaddress_get(iface) : UNKNOWN_VALUE); printf("%sIface Netdev: %s\n", prefix, (strlen(iscsi_iface_netdev_get(iface)) > 0) ? iscsi_iface_netdev_get(iface) : UNKNOWN_VALUE); } struct iface_print_node_data { struct node_rec *last_rec; struct iface_rec *match_iface; }; static int iface_print_nodes(void *data, node_rec_t *rec) { struct iface_print_node_data *print_data = data; if (!iface_match(print_data->match_iface, &rec->iface)) return -1; idbm_print_node_tree(print_data->last_rec, rec, "\t"); return 0; } /** * iface_print_tree - print out binding info * @iface: iface to print out * * Currently this looks like the iface conf print, because we only * have the binding info. When we store the iface specific node settings * in the iface record then it will look different. */ int iface_print_tree(__attribute__((unused))void *data, struct iface_rec *iface) { struct node_rec last_rec; struct iface_print_node_data print_data; int num_found = 0; printf("Iface: %s\n", iface->name); memset(&last_rec, 0, sizeof(struct node_rec )); print_data.match_iface = iface; print_data.last_rec = &last_rec; idbm_for_each_rec(&num_found, &print_data, iface_print_nodes, false); return 0; } void iface_print_flat(struct iscsi_iface *iface) { printf("%s %s,%s,%s,%s,%s\n", _unwrap(iscsi_iface_name_get(iface)), _unwrap(iscsi_iface_transport_name_get(iface)), _unwrap(iscsi_iface_hwaddress_get(iface)), _unwrap(iscsi_iface_ipaddress_get(iface)), _unwrap(iscsi_iface_netdev_get(iface)), _unwrap(iscsi_iface_iname_get(iface))); } int iface_for_each_iface(void *data, int skip_def, int *nr_found, iface_op_fn *fn) { DIR *iface_dirfd; struct dirent *iface_dent; struct iface_rec *iface, *def_iface; int err = 0, i = 0; if (!skip_def) { while ((def_iface = default_ifaces[i++])) { iface = iface_alloc(def_iface->name, &err); if (!iface) { log_error("Could not add iface %s.", def_iface->name); continue; } iface_copy(iface, def_iface); err = fn(data, iface); free(iface); if (err) return err; (*nr_found)++; } } iface_dirfd = opendir(IFACE_CONFIG_DIR); if (!iface_dirfd) return errno; while ((iface_dent = readdir(iface_dirfd))) { if (!strcmp(iface_dent->d_name, ".") || !strcmp(iface_dent->d_name, "..")) continue; if (!strcmp(iface_dent->d_name, "iface.example")) continue; log_debug(5, "iface_for_each_iface found %s", iface_dent->d_name); iface = iface_alloc(iface_dent->d_name, &err); if (!iface || err) { if (err == ISCSI_ERR_INVAL) log_error("Invalid iface name %s. Must be " "from 1 to %d characters.", iface_dent->d_name, ISCSI_MAX_IFACE_LEN - 1); else log_error("Could not add iface %s.", iface_dent->d_name); continue; } err = idbm_lock(); if (err) { free(iface); continue; } err = __iface_conf_read(iface); idbm_unlock(); if (err) { log_error("Could not read def iface %s (err %d)", iface->name, err); free(iface); continue; } if (!iface_is_valid(iface)) { log_debug(5, "iface is not valid " "Iface settings " iface_fmt, iface_str(iface)); free(iface); continue; } err = fn(data, iface); free(iface); if (err) break; (*nr_found)++; } closedir(iface_dirfd); return err; } static int iface_link(void *data, struct iface_rec *iface) { struct list_head *ifaces = data; struct iface_rec *iface_copy; iface_copy = calloc(1, sizeof(*iface_copy)); if (!iface_copy) return ISCSI_ERR_NOMEM; memcpy(iface_copy, iface, sizeof(*iface_copy)); INIT_LIST_HEAD(&iface_copy->list); list_add_tail(&iface_copy->list, ifaces); return 0; } /** * iface_link_ifaces - link non default ifaces * @ifaces: list to add ifaces to * * This will return a list of the ifaces created by iscsiadm * or the user. It does not return the static default ones. */ void iface_link_ifaces(struct list_head *ifaces) { int nr_found = 0; iface_for_each_iface(ifaces, 1, &nr_found, iface_link); } /* * ipv6 address strings will have at least two colons * * NOTE: does NOT validate the IP address */ static bool ipaddr_is_ipv6(char *ipaddr) { char *first_colon, *second_colon; bool res = false; if (ipaddr) { first_colon = strchr(ipaddr, ':'); if (first_colon) { second_colon = strchr(first_colon+1, ':'); if (second_colon && (second_colon != first_colon)) res = true; } log_debug(8, "%s(%s) -> %u", __FUNCTION__, ipaddr, res); } else log_debug(8, "%s(nil) -> %u", __FUNCTION__, res); return res; } /** * iface_setup_from_boot_context - setup iface from boot context info * @iface: iface t setup * @context: boot context info * * Returns 1 if setup for offload. */ int iface_setup_from_boot_context(struct iface_rec *iface, struct boot_context *context) { struct iscsi_transport *t = NULL; uint32_t hostno; if (strlen(context->initiatorname)) strlcpy(iface->iname, context->initiatorname, sizeof(iface->iname)); if (strlen(context->scsi_host_name)) { if (sscanf(context->scsi_host_name, "iscsi_boot%u", &hostno) != 1) { log_error("Could not parse %s's host no.", context->scsi_host_name); return 0; } } else if (strlen(context->iface)) { /* this ifdef is only temp until distros and firmwares are updated */ #ifdef OFFLOAD_BOOT_SUPPORTED char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; int rc; memset(transport_name, 0, ISCSI_TRANSPORT_NAME_MAXLEN); /* make sure offload driver is loaded */ if (!net_get_transport_name_from_netdev(context->iface, transport_name)) t = iscsi_sysfs_get_transport_by_name(transport_name); if (net_ifup_netdev(context->iface)) log_warning("Could not bring up netdev %s for boot", context->iface); hostno = iscsi_sysfs_get_host_no_from_hwaddress(context->mac, &rc); if (rc) { /* * If the MAC in the boot info does not match an iscsi * host then the MAC must be for network card, so boot * is not going to be offloaded. */ log_debug(3, "Could not match %s to host", context->mac); return 0; } strlcpy(iface->netdev, context->iface, sizeof(iface->netdev)); #else return 0; #endif } else return 0; /* * set up for access through a offload card. */ if (!t) t = iscsi_sysfs_get_transport_by_hba(hostno); if (!t) { log_error("Could not get transport for host%u. " "Make sure the iSCSI driver is loaded.", hostno); return 0; } strcpy(iface->transport_name, t->name); strlcpy(iface->hwaddress, context->mac, sizeof(iface->hwaddress)); strlcpy(iface->ipaddress, context->ipaddr, sizeof(iface->ipaddress)); iface->vlan_id = atoi(context->vlan); strlcpy(iface->subnet_mask, context->mask, sizeof(iface->subnet_mask)); strlcpy(iface->gateway, context->gateway, sizeof(iface->gateway)); snprintf(iface->name, sizeof(iface->name), "%s.%s.%s.%u", iface->transport_name, context->mac, ipaddr_is_ipv6(iface->ipaddress) ? "ipv6" : "ipv4", iface->iface_num); log_debug(1, "iface " iface_fmt "", iface_str(iface)); return 1; } /** * iface_create_ifaces_from_boot_contexts - create ifaces based on boot info * @ifaces: list to store ifaces in * @targets: list of targets to create ifaces from * * This function will create an iface struct based on the boot info * and it will create (or update if existing already) an iface rec in * the ifaces dir based on the info. */ int iface_create_ifaces_from_boot_contexts(struct list_head *ifaces, struct list_head *targets) { struct boot_context *context; struct iface_rec *iface, *tmp_iface; int rc = 0; list_for_each_entry(context, targets, list) { rc = 0; /* use dummy name. If valid it will get overwritten below */ iface = iface_alloc(DEFAULT_IFACENAME, &rc); if (!iface) { log_error("Could not setup iface %s for boot", context->iface); goto fail; } if (!iface_setup_from_boot_context(iface, context)) { /* no offload so forget it */ free(iface); continue; } rc = iface_conf_write(iface); if (rc) { log_error("Could not setup default iface conf " "for %s.", iface->name); free(iface); goto fail; } list_add_tail(&iface->list, ifaces); } return 0; fail: list_for_each_entry_safe(iface, tmp_iface, ifaces, list) { list_del(&iface->list); free(iface); } return rc; } struct iface_param_count { struct iface_rec *primary; int count; }; #define IFACE_NET_PARAM_EN_CNT(param_val, cnt) { \ if (!strcmp(param_val, "disable") || \ !strcmp(param_val, "enable")) \ (*cnt)++; \ } /** * iface_get_common_param_count - Gets common parameters count for given iface * @iface: iface to setup * @count: number of parameters to set */ static void iface_get_common_param_count(struct iface_rec *iface, int *count) { if (strcmp(iface->vlan_state, "disable")) { /* vlan_state enabled */ (*count)++; if (iface->vlan_id) /* For vlan value */ (*count)++; } else { /* vlan_state disabled */ (*count)++; } if (iface->mtu) (*count)++; if (iface->port) (*count)++; IFACE_NET_PARAM_EN_CNT(iface->delayed_ack, count); IFACE_NET_PARAM_EN_CNT(iface->nagle, count); IFACE_NET_PARAM_EN_CNT(iface->tcp_wsf_state, count); IFACE_NET_PARAM_EN_CNT(iface->tcp_timestamp, count); IFACE_NET_PARAM_EN_CNT(iface->redirect, count); IFACE_NET_PARAM_EN_CNT(iface->header_digest, count); IFACE_NET_PARAM_EN_CNT(iface->data_digest, count); IFACE_NET_PARAM_EN_CNT(iface->immediate_data, count); IFACE_NET_PARAM_EN_CNT(iface->initial_r2t, count); IFACE_NET_PARAM_EN_CNT(iface->data_seq_inorder, count); IFACE_NET_PARAM_EN_CNT(iface->data_pdu_inorder, count); IFACE_NET_PARAM_EN_CNT(iface->chap_auth, count); IFACE_NET_PARAM_EN_CNT(iface->bidi_chap, count); IFACE_NET_PARAM_EN_CNT(iface->strict_login_comp, count); IFACE_NET_PARAM_EN_CNT(iface->discovery_auth, count); IFACE_NET_PARAM_EN_CNT(iface->discovery_logout, count); if (iface->tcp_wsf) (*count)++; if (iface->tcp_timer_scale) (*count)++; if (iface->def_task_mgmt_tmo) (*count)++; if (iface->erl) (*count)++; if (iface->max_recv_dlength) (*count)++; if (iface->first_burst_len) (*count)++; if (iface->max_burst_len) (*count)++; if (iface->max_out_r2t) (*count)++; } /** * __iface_get_param_count - Gets netconfig parameter count for given iface * @data: iface_param_count structure * @iface: iface to setup */ static int __iface_get_param_count(void *data, struct iface_rec *iface) { struct iface_param_count *iface_params = data; enum iscsi_iface_type iptype; int count = 0; if (strcmp(iface_params->primary->hwaddress, iface->hwaddress)) return 0; iptype = iface_get_iptype(iface); if (iptype == ISCSI_IFACE_TYPE_IPV4) { if (strcmp(iface->state, "disable")) { if (strstr(iface->bootproto, "dhcp")) { /* DHCP enabled */ count++; } else { /* DHCP disabled */ count++; if (strstr(iface->ipaddress, ".")) { /* User configured IPv4 Address */ count++; if (strstr(iface->subnet_mask, ".")) /* User configured Subnet */ count++; if (strstr(iface->gateway, ".")) /* User configured Gateway */ count++; } else { /* * IPv4 Address not valid, decrement * count of DHCP */ count--; } } /* * If IPv4 configuration in iface file is valid, * enable state and other parameters (if any) */ if (count) { /* iface state */ count++; IFACE_NET_PARAM_EN_CNT(iface->dhcp_dns, &count); IFACE_NET_PARAM_EN_CNT(iface->dhcp_slp_da, &count); IFACE_NET_PARAM_EN_CNT(iface->tos_state, &count); IFACE_NET_PARAM_EN_CNT(iface->gratuitous_arp, &count); IFACE_NET_PARAM_EN_CNT( iface->dhcp_alt_client_id_state, &count); if (iface->dhcp_alt_client_id[0]) count++; IFACE_NET_PARAM_EN_CNT( iface->dhcp_req_vendor_id_state, &count); IFACE_NET_PARAM_EN_CNT( iface->dhcp_vendor_id_state, &count); if (iface->dhcp_vendor_id[0]) count++; IFACE_NET_PARAM_EN_CNT(iface->dhcp_learn_iqn, &count); IFACE_NET_PARAM_EN_CNT(iface->fragmentation, &count); IFACE_NET_PARAM_EN_CNT( iface->incoming_forwarding, &count); if (iface->tos) count++; if (iface->ttl) count++; iface_get_common_param_count(iface, &count); } } else { /* IPv4 is disabled, iface state */ count++; } } else if (iptype == ISCSI_IFACE_TYPE_IPV6) { if (strcmp(iface->state, "disable")) { /* IPv6 Address */ if (strstr(iface->ipv6_autocfg, "nd") || strstr(iface->ipv6_autocfg, "dhcpv6")) { /* Autocfg enabled */ count++; } else { /* Autocfg disabled */ count++; if (strstr(iface->ipaddress, ":")) /* User configured IPv6 Address */ count++; else /* * IPv6 Address not valid, decrement * count of IPv6 Autocfg */ count--; } /* IPv6 LinkLocal Address */ if (strstr(iface->linklocal_autocfg, "auto")) /* Autocfg enabled */ count++; else { /* Autocfg disabled */ count++; if (strstr(iface->ipv6_linklocal, ":")) /* User configured LinkLocal Address */ count++; else /* * LinkLocal Address not valid, * decrement count of LinkLocal Autocfg */ count--; } /* IPv6 Router Address */ if (strstr(iface->router_autocfg, "auto")) /* Autocfg enabled */ count++; else { /* Autocfg disabled */ count++; if (strstr(iface->ipv6_router, ":")) /* User configured Router Address */ count++; else /* * Router Address not valid, * decrement count of Router Autocfg */ count--; } /* * If IPv6 configuration in iface file is valid, * enable state and other parameters (if any) */ if (count) { /* iface state */ count++; IFACE_NET_PARAM_EN_CNT( iface->gratuitous_neighbor_adv, &count); IFACE_NET_PARAM_EN_CNT(iface->mld, &count); if (iface->flow_label) count++; if (iface->traffic_class) count++; if (iface->hop_limit) count++; if (iface->nd_reachable_tmo) count++; if (iface->nd_rexmit_time) count++; if (iface->nd_stale_tmo) count++; if (iface->dup_addr_detect_cnt) count++; if (iface->router_adv_link_mtu) count++; iface_get_common_param_count(iface, &count); } } else { /* IPv6 is disabled, iface state */ count++; } } iface_params->count += count; return 0; } /** * iface_get_param_count - Gets netconfig parameter count from iface * @iface: iface to setup * @iface_all: Flag for number of ifaces to traverse (1 for all) * * Returns netconfig parameter count. */ int iface_get_param_count(struct iface_rec *iface, int iface_all) { int num_found = 0, rc; struct iface_param_count iface_params; log_debug(8, "In iface_get_param_count"); iface_params.primary = iface; iface_params.count = 0; if (iface_all) rc = iface_for_each_iface(&iface_params, 0, &num_found, __iface_get_param_count); else rc = __iface_get_param_count(&iface_params, iface); log_debug(8, "iface_get_param_count: rc = %d, count = %d", rc, iface_params.count); return iface_params.count; } /* write integer parameter value */ static int iface_fill_int_param_val(struct iovec *iov, uint32_t iface_num, uint8_t iface_type, uint16_t param, uint8_t param_type, uint32_t param_len, uint32_t param_val) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; uint8_t val8 = 0; uint16_t val16 = 0; uint32_t val32 = 0; char *val = NULL; len = sizeof(struct iscsi_iface_param_info) + param_len; iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->iface_num = iface_num; net_param->len = param_len; net_param->param = param; net_param->iface_type = iface_type; net_param->param_type = param_type; switch (param_len) { case 1: val8 = (uint8_t)param_val; val = (char *)&val8; break; case 2: val16 = (uint16_t)param_val; val = (char *)&val16; break; case 4: val32 = (uint32_t)param_val; val = (char *)&val32; break; default: goto free; } memcpy(net_param->value, val, param_len); return 0; free: free(iov->iov_base); iov->iov_base = NULL; iov->iov_len = 0; return 1; } #define IFACE_SET_PARAM_INTVAL(iov, inum, itype, param, ptype, plen, \ ival, gcnt, lcnt) { \ if (ival && !iface_fill_int_param_val(iov, inum, itype, param, \ ptype, plen, ival)) { \ (*gcnt)++; \ (*lcnt)++; \ } \ } /* IPv4/IPv6 VLAN_ID: decimal value <= 4095 */ static int iface_fill_vlan_id(struct iovec *iov, struct iface_rec *iface, uint32_t iface_type) { int len; struct iscsi_iface_param_info *net_param; uint16_t vlan = 0; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 2; iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_VLAN_TAG, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = ISCSI_NET_PARAM_VLAN_TAG; net_param->iface_type = iface_type; net_param->iface_num = iface->iface_num; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 2; if (iface->vlan_id <= ISCSI_MAX_VLAN_ID && iface->vlan_priority <= ISCSI_MAX_VLAN_PRIORITY) /* * Bit 15-13: User Priority of VLAN * Bit 11-00: VLAN ID */ vlan = (iface->vlan_priority << 13) | (iface->vlan_id & ISCSI_MAX_VLAN_ID); memcpy(net_param->value, &vlan, net_param->len); return 0; } /* disable/enable parameters */ static int iface_fill_param_state(struct iovec *iov, uint32_t iface_num, uint8_t iface_type, uint16_t param, uint8_t param_type, char *param_val) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; if (!param_val[0]) return 1; len = sizeof(struct iscsi_iface_param_info) + 1; iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->iface_num = iface_num; net_param->len = 1; net_param->param = param; net_param->iface_type = iface_type; net_param->param_type = param_type; if (!strcmp(param_val, "disable")) net_param->value[0] = ISCSI_NET_PARAM_DISABLE; else if (!strcmp(param_val, "enable")) net_param->value[0] = ISCSI_NET_PARAM_ENABLE; else goto free; return 0; free: free(iov->iov_base); iov->iov_base = NULL; iov->iov_len = 0; return 1; } #define IFACE_SET_PARAM_STATE(iov, inum, itype, param, ptype, ival, \ gcnt, lcnt) { \ if (!iface_fill_param_state(iov, inum, itype, param, ptype, \ ival)) { \ (*gcnt)++; \ (*lcnt)++; \ } \ } /* IPv4 Bootproto: DHCP/static */ static int iface_fill_net_bootproto(struct iovec *iov, struct iface_rec *iface) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 1; iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV4_BOOTPROTO, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = ISCSI_NET_PARAM_IPV4_BOOTPROTO; net_param->iface_type = ISCSI_IFACE_TYPE_IPV4; net_param->iface_num = iface->iface_num; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 1; if (!strcmp(iface->bootproto, "dhcp")) net_param->value[0] = ISCSI_BOOTPROTO_DHCP; else net_param->value[0] = ISCSI_BOOTPROTO_STATIC; return 0; } /* IPv6 IPAddress Autocfg: nd/dhcpv6/disable */ static int iface_fill_net_autocfg(struct iovec *iov, struct iface_rec *iface) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 1; iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG; net_param->iface_type = ISCSI_IFACE_TYPE_IPV6; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 1; if (!strcmp(iface->ipv6_autocfg, "nd")) net_param->value[0] = ISCSI_IPV6_AUTOCFG_ND_ENABLE; else if (!strcmp(iface->ipv6_autocfg, "dhcpv6")) net_param->value[0] = ISCSI_IPV6_AUTOCFG_DHCPV6_ENABLE; else net_param->value[0] = ISCSI_IPV6_AUTOCFG_DISABLE; return 0; } /* IPv6 LinkLocal Autocfg: enable/disable */ static int iface_fill_linklocal_autocfg(struct iovec *iov, struct iface_rec *iface) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 1; iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG; net_param->iface_type = ISCSI_IFACE_TYPE_IPV6; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 1; if (strstr(iface->linklocal_autocfg, "auto")) net_param->value[0] = ISCSI_IPV6_LINKLOCAL_AUTOCFG_ENABLE; else net_param->value[0] = ISCSI_IPV6_LINKLOCAL_AUTOCFG_DISABLE; return 0; } /* IPv6 Router Autocfg: enable/disable */ static int iface_fill_router_autocfg(struct iovec *iov, struct iface_rec *iface) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 1; iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG; net_param->iface_type = ISCSI_IFACE_TYPE_IPV6; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 1; if (strstr(iface->router_autocfg, "auto")) net_param->value[0] = ISCSI_IPV6_ROUTER_AUTOCFG_ENABLE; else net_param->value[0] = ISCSI_IPV6_ROUTER_AUTOCFG_DISABLE; return 0; } /* IPv4 IPAddress/Subnet Mask/Gateway: 4 bytes */ static int iface_fill_net_ipv4_addr(struct iovec *iov, uint32_t iface_num, uint16_t param, char *param_val) { int rc = 1; int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 4; iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = param; net_param->iface_type = ISCSI_IFACE_TYPE_IPV4; net_param->iface_num = iface_num; net_param->len = 4; net_param->param_type = ISCSI_NET_PARAM; rc = inet_pton(AF_INET, param_val, net_param->value); if (rc <= 0) goto free; /* validate */ if (!net_param->value[0] && !net_param->value[1] && !net_param->value[2] && !net_param->value[3]) goto free; return 0; free: free(iov->iov_base); iov->iov_base = NULL; iov->iov_len = 0; return 1; } #define IFACE_SET_NET_PARAM_IPV4_ADDR(iov, inum, param, ival, gcnt, \ lcnt) { \ if (strstr(ival, ".")) { \ if (!iface_fill_net_ipv4_addr(iov, inum, param, ival)) {\ (*gcnt)++; \ (*lcnt)++; \ } \ } \ } /* IPv6 IPAddress/LinkLocal/Router: 16 bytes */ static int iface_fill_net_ipv6_addr(struct iovec *iov, uint32_t iface_num, uint16_t param, char *param_val) { int rc; int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; len = sizeof(struct iscsi_iface_param_info) + 16; iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = param; net_param->iface_type = ISCSI_IFACE_TYPE_IPV6; net_param->iface_num = iface_num; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 16; rc = inet_pton(AF_INET6, param_val, net_param->value); if (rc <= 0) goto free; return 0; free: free(iov->iov_base); iov->iov_base = NULL; iov->iov_len = 0; return 1; } #define IFACE_SET_NET_PARAM_IPV6_ADDR(iov, inum, param, ival, gcnt, \ lcnt) { \ if (strstr(ival, ":")) { \ if (!iface_fill_net_ipv6_addr(iov, inum, param, ival)) {\ (*gcnt)++; \ (*lcnt)++; \ } \ } \ } /* write string parameter value */ static int iface_fill_str_param_val(struct iovec *iov, uint32_t iface_num, uint8_t iface_type, uint16_t param, uint32_t param_len, char *param_val) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; if (!param_val[0]) return 1; len = sizeof(struct iscsi_iface_param_info) + param_len; iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->iface_num = iface_num; net_param->len = param_len; net_param->param = param; net_param->iface_type = iface_type; net_param->param_type = ISCSI_NET_PARAM; memcpy(net_param->value, param_val, param_len); return 0; } #define IFACE_SET_NET_PARAM_STRVAL(iov, inum, itype, param, plen, \ ival, gcnt, lcnt) { \ if (!iface_fill_str_param_val(iov, inum, itype, param, plen, \ ival)) { \ (*gcnt)++; \ (*lcnt)++; \ } \ } struct iface_net_config { struct iface_rec *primary; struct iovec *iovs; int count; }; static int __iface_build_net_config(void *data, struct iface_rec *iface) { struct iface_net_config *net_config = data; struct iovec *iov; int iptype = ISCSI_IFACE_TYPE_IPV4; int count = 0; if (strcmp(net_config->primary->hwaddress, iface->hwaddress)) return 0; /* start at 2, because 0 is for nlmsghdr and 1 for event */ iov = net_config->iovs + 2; if (!iface->port) iface->port = 3260; iptype = iface_get_iptype(iface); switch (iptype) { case ISCSI_IFACE_TYPE_IPV4: if (!strcmp(iface->state, "disable")) { IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IFACE_ENABLE, ISCSI_NET_PARAM, iface->state, &net_config->count, &count); return 0; } if (strstr(iface->bootproto, "dhcp")) { if (!iface_fill_net_bootproto(&iov[net_config->count], iface)) { net_config->count++; count++; } } else if (strstr(iface->ipaddress, ".")) { if (!iface_fill_net_bootproto(&iov[net_config->count], iface)) { net_config->count++; count++; } IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count], iface->iface_num, ISCSI_NET_PARAM_IPV4_ADDR, iface->ipaddress, &net_config->count, &count); IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count], iface->iface_num, ISCSI_NET_PARAM_IPV4_SUBNET, iface->subnet_mask, &net_config->count, &count); IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count], iface->iface_num, ISCSI_NET_PARAM_IPV4_GW, iface->gateway, &net_config->count, &count); } /* * If IPv4 configuration in iface file is valid, * fill state and other parameters (if any) */ if (count) { IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN, ISCSI_NET_PARAM, iface->dhcp_dns, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN, ISCSI_NET_PARAM, iface->dhcp_slp_da, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_TOS_EN, ISCSI_NET_PARAM, iface->tos_state, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_TOS, ISCSI_NET_PARAM, 1, iface->tos, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN, ISCSI_NET_PARAM, iface->gratuitous_arp, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN, ISCSI_NET_PARAM, iface->dhcp_alt_client_id_state, &net_config->count, &count); IFACE_SET_NET_PARAM_STRVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID, strlen(iface->dhcp_alt_client_id), iface->dhcp_alt_client_id, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN, ISCSI_NET_PARAM, iface->dhcp_req_vendor_id_state, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN, ISCSI_NET_PARAM, iface->dhcp_vendor_id_state, &net_config->count, &count); IFACE_SET_NET_PARAM_STRVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID, strlen(iface->dhcp_vendor_id), iface->dhcp_vendor_id, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN, ISCSI_NET_PARAM, iface->dhcp_learn_iqn, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE, ISCSI_NET_PARAM, iface->fragmentation, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN, ISCSI_NET_PARAM, iface->incoming_forwarding, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV4, ISCSI_NET_PARAM_IPV4_TTL, ISCSI_NET_PARAM, 1, iface->ttl, &net_config->count, &count); } break; case ISCSI_IFACE_TYPE_IPV6: if (!strcmp(iface->state, "disable")) { IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IFACE_ENABLE, ISCSI_NET_PARAM, iface->state, &net_config->count, &count); return 0; } /* For IPv6 Address */ if (strstr(iface->ipv6_autocfg, "nd") || strstr(iface->ipv6_autocfg, "dhcpv6")) { if (!iface_fill_net_autocfg(&iov[net_config->count], iface)) { net_config->count++; count++; } } else if (strstr(iface->ipaddress, ":")) { if (!iface_fill_net_autocfg(&iov[net_config->count], iface)) { net_config->count++; count++; } /* User provided IPv6 Address */ IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count], iface->iface_num, ISCSI_NET_PARAM_IPV6_ADDR, iface->ipaddress, &net_config->count, &count); } /* For LinkLocal Address */ if (strstr(iface->linklocal_autocfg, "auto")) { if (!iface_fill_linklocal_autocfg( &iov[net_config->count], iface)) { net_config->count++; count++; } } else if (strstr(iface->ipv6_linklocal, ":")) { if (!iface_fill_linklocal_autocfg( &iov[net_config->count], iface)) { net_config->count++; count++; } /* User provided Link Local Address */ IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count], iface->iface_num, ISCSI_NET_PARAM_IPV6_LINKLOCAL, iface->ipv6_linklocal, &net_config->count, &count); } /* For Router Address */ if (strstr(iface->router_autocfg, "auto")) { if (!iface_fill_router_autocfg(&iov[net_config->count], iface)) { net_config->count++; count++; } } else if (strstr(iface->ipv6_router, ":")) { if (!iface_fill_router_autocfg(&iov[net_config->count], iface)) { net_config->count++; count++; } /* User provided Router Address */ IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count], iface->iface_num, ISCSI_NET_PARAM_IPV6_ROUTER, iface->ipv6_router, &net_config->count, &count); } /* * If IPv6 configuration in iface file is valid, * fill state and other parameters */ if (count) { IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN, ISCSI_NET_PARAM, iface->gratuitous_neighbor_adv, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_MLD_EN, ISCSI_NET_PARAM, iface->mld, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_FLOW_LABEL, ISCSI_NET_PARAM, 4, iface->flow_label, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS, ISCSI_NET_PARAM, 1, iface->traffic_class, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_HOP_LIMIT, ISCSI_NET_PARAM, 1, iface->hop_limit, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO, ISCSI_NET_PARAM, 4, iface->nd_reachable_tmo, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME, ISCSI_NET_PARAM, 4, iface->nd_rexmit_time, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_ND_STALE_TMO, ISCSI_NET_PARAM, 4, iface->nd_stale_tmo, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT, ISCSI_NET_PARAM, 1, iface->dup_addr_detect_cnt, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, ISCSI_IFACE_TYPE_IPV6, ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU, ISCSI_NET_PARAM, 4, iface->router_adv_link_mtu, &net_config->count, &count); } break; } /* Fill parameters common to IPv4 and IPv6 ifaces */ if (count) { IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_IFACE_ENABLE, ISCSI_NET_PARAM, iface->state, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_VLAN_ENABLED, ISCSI_NET_PARAM, iface->vlan_state, &net_config->count, &count); if (strcmp(iface->vlan_state, "disable") && iface->vlan_id) { if (!iface_fill_vlan_id(&iov[net_config->count], iface, iptype)) { net_config->count++; count++; } } IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_MTU, ISCSI_NET_PARAM, 2, iface->mtu, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_PORT, ISCSI_NET_PARAM, 2, iface->port, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_DELAYED_ACK_EN, ISCSI_NET_PARAM, iface->delayed_ack, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_TCP_NAGLE_DISABLE, ISCSI_NET_PARAM, iface->nagle, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_TCP_WSF_DISABLE, ISCSI_NET_PARAM, iface->tcp_wsf_state, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_TCP_WSF, ISCSI_NET_PARAM, 1, iface->tcp_wsf, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_TCP_TIMER_SCALE, ISCSI_NET_PARAM, 1, iface->tcp_timer_scale, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_TCP_TIMESTAMP_EN, ISCSI_NET_PARAM, iface->tcp_timestamp, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_NET_PARAM_REDIRECT_EN, ISCSI_NET_PARAM, iface->redirect, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO, ISCSI_IFACE_PARAM, 2, iface->def_task_mgmt_tmo, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_HDRDGST_EN, ISCSI_IFACE_PARAM, iface->header_digest, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_DATADGST_EN, ISCSI_IFACE_PARAM, iface->data_digest, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_IMM_DATA_EN, ISCSI_IFACE_PARAM, iface->immediate_data, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_INITIAL_R2T_EN, ISCSI_IFACE_PARAM, iface->initial_r2t, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN, ISCSI_IFACE_PARAM, iface->data_seq_inorder, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_PDU_INORDER_EN, ISCSI_IFACE_PARAM, iface->data_pdu_inorder, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_ERL, ISCSI_IFACE_PARAM, 1, iface->erl, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH, ISCSI_IFACE_PARAM, 4, iface->max_recv_dlength, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_FIRST_BURST, ISCSI_IFACE_PARAM, 4, iface->first_burst_len, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_MAX_R2T, ISCSI_IFACE_PARAM, 2, iface->max_out_r2t, &net_config->count, &count); IFACE_SET_PARAM_INTVAL(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_MAX_BURST, ISCSI_IFACE_PARAM, 4, iface->max_burst_len, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_CHAP_AUTH_EN, ISCSI_IFACE_PARAM, iface->chap_auth, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_BIDI_CHAP_EN, ISCSI_IFACE_PARAM, iface->bidi_chap, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN, ISCSI_IFACE_PARAM, iface->strict_login_comp, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL, ISCSI_IFACE_PARAM, iface->discovery_auth, &net_config->count, &count); IFACE_SET_PARAM_STATE(&iov[net_config->count], iface->iface_num, iptype, ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN, ISCSI_IFACE_PARAM, iface->discovery_logout, &net_config->count, &count); } return 0; } /** * iface_build_net_config - Setup neconfig parameter buffers * @iface: iface to setup * @iface_all: Flag for number of ifaces to traverse (1 for all) * @iovs: iovec buffer for netconfig parameters * * Returns total number of netconfig parameter buffers used. */ int iface_build_net_config(struct iface_rec *iface, int iface_all, struct iovec *iovs) { int num_found = 0, rc; struct iface_net_config net_config; log_debug(8, "In iface_build_net_config"); net_config.primary = iface; net_config.iovs = iovs; net_config.count = 0; if (iface_all) rc = iface_for_each_iface(&net_config, 0, &num_found, __iface_build_net_config); else rc = __iface_build_net_config(&net_config, iface); log_debug(8, "iface_build_net_config: rc = %d, count = %d", rc, net_config.count); return net_config.count; }