void NetLinkManager::run() { struct nl_handle *handle = nl_handle_alloc(); nl_connect(handle, NETLINK_ROUTE); // init route messages nl_socket_add_membership(handle, RTNLGRP_IPV4_IFADDR); // IPv6 requires further support in the parsing procedures! // nl_socket_add_membership(handle, RTNLGRP_IPV6_IFADDR); nl_socket_add_membership(handle, RTNLGRP_LINK); // add netlink fd to vsocket _sock->add(nl_socket_get_fd(handle)); try { while (_initialized) { std::list<int> fds; _sock->select(fds, NULL); int fd = fds.front(); // create a new event object NetLinkManagerEvent lme(fd); // ignore if the event is unknown if (lme.getType() == LinkManagerEvent::EVENT_UNKOWN) continue; // ignore if this is an wireless extension event if (lme.isWirelessExtension()) continue; // need to refresh the cache { ibrcommon::MutexLock l(_call_mutex); _refresh_cache = true; } // print out some debugging IBRCOMMON_LOGGER_DEBUG(10) << lme.toString() << IBRCOMMON_LOGGER_ENDL; // notify all subscribers about this event raiseEvent(lme); } } catch (const vsocket_exception&) { // stopped / interrupted IBRCOMMON_LOGGER(error) << "NetLink connection stopped" << IBRCOMMON_LOGGER_ENDL; } nl_close(handle); nl_handle_destroy(handle); }
static bool nl_new(struct dionaea *d) { g_debug("%s", __PRETTY_FUNCTION__); nl_runtime.sock = nl_socket_alloc(); struct nl_sock *sock = nl_runtime.sock; nl_socket_disable_seq_check(sock); nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, nl_event_input, NULL); nl_join_groups(sock, RTMGRP_LINK); int err; if ( (err = nl_connect(sock, NETLINK_ROUTE)) < 0) { g_error("Could not connect netlink (%s)", nl_geterror(err)); } nl_socket_add_membership(sock, RTNLGRP_LINK); nl_socket_add_membership(sock, RTNLGRP_NEIGH); nl_socket_add_membership(sock, RTNLGRP_IPV4_IFADDR); nl_socket_add_membership(sock, RTNLGRP_IPV6_IFADDR); if( (err=rtnl_neigh_alloc_cache(sock, &nl_runtime.neigh_cache)) != 0 ) { g_error("Could not allocate neigh cache! (%s)", nl_geterror(err)); } #if LIBNL_RTNL_LINK_ALLOC_CACHE_ARGC == 3 if( (err=rtnl_link_alloc_cache(sock, AF_UNSPEC, &nl_runtime.link_cache)) != 0 ) #elif LIBNL_RTNL_LINK_ALLOC_CACHE_ARGC == 2 if( (err=rtnl_link_alloc_cache(sock, &nl_runtime.link_cache)) != 0 ) #endif { g_error("Could not allocate link cache! (%s)", nl_geterror(err)); } if( (err=rtnl_addr_alloc_cache(sock, &nl_runtime.addr_cache)) != 0 ) { g_error("Could not allocate addr cache! (%s)", nl_geterror(err)); } nl_cache_mngt_provide(nl_runtime.neigh_cache); nl_cache_mngt_provide(nl_runtime.link_cache); nl_cache_mngt_provide(nl_runtime.addr_cache); nl_runtime.ihandler = ihandler_new("dionaea.connection.*.accept", nl_ihandler_cb, NULL); ev_io_init(&nl_runtime.io_in, nl_io_in_cb, nl_socket_get_fd(sock), EV_READ); ev_io_start(g_dionaea->loop, &nl_runtime.io_in); nl_runtime.link_addr_cache = g_hash_table_new(g_int_hash, g_int_equal); nl_cache_foreach(nl_runtime.link_cache, nl_obj_input, NULL); nl_cache_foreach(nl_runtime.addr_cache, nl_obj_input, NULL); return true; }
gboolean nm_netlink_monitor_subscribe (NMNetlinkMonitor *self, int group, GError **error) { NMNetlinkMonitorPrivate *priv; int subs, err; g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); if (!priv->nlh_event) { if (!nm_netlink_monitor_open_connection (self, error)) return FALSE; } subs = get_subs (self, group) + 1; if (subs == 1) { err = nl_socket_add_membership (priv->nlh_event, group); if (err < 0) { g_set_error (error, NM_NETLINK_MONITOR_ERROR, NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP, _("unable to join netlink group: %s"), nl_geterror (err)); return FALSE; } } /* Update # of subscriptions for this group */ set_subs (self, group, subs); return TRUE; }
int iface_mon_start(iface_mon_cb cb) { int err; iface_mon_sock = nl_socket_alloc(); if (!iface_mon_sock) { fprintf(stderr, "Failed to allocate netlink socket.\n"); return -ENOMEM; } nl_socket_disable_seq_check(iface_mon_sock); nl_socket_modify_cb(iface_mon_sock, NL_CB_VALID, NL_CB_CUSTOM, iface_mon_handler, cb); if (nl_connect(iface_mon_sock, NETLINK_ROUTE)) { fprintf(stderr, "Failed to connect to generic netlink.\n"); err = -ENOLINK; goto out_handle_destroy; } nl_socket_add_membership(iface_mon_sock, RTNLGRP_LINK); return 0; out_handle_destroy: nl_socket_free(iface_mon_sock); return err; }
gboolean nm_netlink_listener_subscribe (NMNetlinkListener *listener, int group, GError **error) { NMNetlinkListenerPrivate *priv; g_return_val_if_fail (NM_IS_NETLINK_LISTENER (listener), FALSE); priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener); if (!priv->nlh) { if (!open_connection (listener, error)) return FALSE; } if (nl_socket_add_membership (priv->nlh, group) < 0) { g_set_error (error, NM_NETLINK_LISTENER_ERROR, NM_NETLINK_LISTENER_ERROR_NETLINK_JOIN_GROUP, _("unable to join netlink group: %s"), nl_geterror ()); return FALSE; } return TRUE; }
static int prepare_listen_events(void) { int mcid, ret; /* Configuration multicast group */ mcid = nl_get_multicast_id(state.nl_sock, "nl80211", "config"); if (mcid < 0) return mcid; ret = nl_socket_add_membership(state.nl_sock, mcid); if (ret) return ret; /* Scan multicast group */ mcid = nl_get_multicast_id(state.nl_sock, "nl80211", "scan"); if (mcid >= 0) { ret = nl_socket_add_membership(state.nl_sock, mcid); if (ret) return ret; } /* Regulatory multicast group */ mcid = nl_get_multicast_id(state.nl_sock, "nl80211", "regulatory"); if (mcid >= 0) { ret = nl_socket_add_membership(state.nl_sock, mcid); if (ret) return ret; } /* MLME multicast group */ mcid = nl_get_multicast_id(state.nl_sock, "nl80211", "mlme"); if (mcid >= 0) { ret = nl_socket_add_membership(state.nl_sock, mcid); if (ret) return ret; } mcid = nl_get_multicast_id(state.nl_sock, "nl80211", "vendor"); if (mcid >= 0) { ret = nl_socket_add_membership(state.nl_sock, mcid); if (ret) return ret; } return 0; }
int unl_genl_subscribe(struct unl *unl, const char *name) { int mcid; mcid = unl_genl_multicast_id(unl, name); if (mcid < 0) return mcid; return nl_socket_add_membership(unl->sock, mcid); }
static struct nl_handle *init_netlink(void) { struct nl_handle *handle; int ret, multicast_id; handle = nl_handle_alloc(); if (!handle){ elog("Cannot allocate netlink handle!\n"); return NULL; } nl_disable_sequence_check(handle); ret = genl_connect(handle); if (ret < 0){ elog("Cannot connect to netlink socket\n"); return NULL; } /* familyid = genl_ctrl_resolve(handle, SAMPLE_NL_FAMILY_NAME); if (!familyid){ elog("Cannot resolve family name(%s)\n", SAMPLE_NL_FAMILY_NAME); return NULL; } dlog("familyid %d\n", familyid); */ multicast_id = nl_get_multicast_id(handle, SAMPLE_NL_FAMILY_NAME, SAMPLE_NL_GRP_NAME); if (multicast_id < 0){ elog("Cannot resolve grp name(%d)\n", SAMPLE_NL_GRP_NAME); return NULL; } ret = nl_socket_add_membership(handle, multicast_id); if (ret < 0){ elog("Cannot join fs multicast group\n"); return NULL; } ret = nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, sample_nl_cb, NULL); if (ret < 0){ elog("Cannot register callback for" " netlink messages\n"); return NULL; } return handle; }
static int wifi_add_membership(wifi_handle handle, const char *group) { hal_info *info = getHalInfo(handle); int id = wifi_get_multicast_id(handle, "nl80211", group); if (id < 0) { ALOGE("Could not find group %s", group); return id; } int ret = nl_socket_add_membership(info->event_sock, id); if (ret < 0) { ALOGE("Could not add membership to group %s", group); } return ret; }
static wifi_error wifi_init_user_sock(hal_info *info) { struct nl_sock *user_sock = wifi_create_nl_socket(WIFI_HAL_USER_SOCK_PORT, NETLINK_USERSOCK); if (user_sock == NULL) { ALOGE("Could not create diag sock"); return WIFI_ERROR_UNKNOWN; } /* Set the socket buffer size */ if (nl_socket_set_buffer_size(user_sock, (256*1024), 0) < 0) { ALOGE("Could not set size for user_sock: %s", strerror(errno)); /* continue anyway with the default (smaller) buffer */ } else { ALOGV("nl_socket_set_buffer_size successful for user_sock"); } struct nl_cb *cb = nl_socket_get_cb(user_sock); if (cb == NULL) { ALOGE("Could not get cb"); return WIFI_ERROR_UNKNOWN; } info->user_sock_arg = 1; nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &info->user_sock_arg); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &info->user_sock_arg); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &info->user_sock_arg); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, user_sock_message_handler, info); nl_cb_put(cb); int ret = nl_socket_add_membership(user_sock, 1); if (ret < 0) { ALOGE("Could not add membership"); return WIFI_ERROR_UNKNOWN; } info->user_sock = user_sock; ALOGV("Initiialized diag sock successfully"); return WIFI_SUCCESS; }
static ni_bool_t __ni_rtevent_join_group(ni_rtevent_handle_t *handle, unsigned int group) { int ret; if (!group || !handle || !handle->nlsock) return FALSE; if (ni_uint_array_contains(&handle->groups, group)) return TRUE; if (!ni_uint_array_append(&handle->groups, group)) return FALSE; ret = nl_socket_add_membership(handle->nlsock, group); if (ret != NLE_SUCCESS) { /* remove from array? */ ni_error("Cannot add rtnetlink group %u membership: %s", group, nl_geterror(ret)); return FALSE; } return TRUE; }
/** * Add cache to cache manager * @arg mngr Cache manager. * @arg cache Cache to be added to cache manager * @arg cb Function to be called upon changes. * @arg data Argument passed on to change callback * * Adds cache to the manager. The operation will trigger a full * dump request from the kernel to initially fill the contents * of the cache. The manager will subscribe to the notification group * of the cache and keep track of any further changes. * * The user is responsible for calling nl_cache_mngr_poll() or monitor * the socket and call nl_cache_mngr_data_ready() to allow the library * to process netlink notification events. * * @see nl_cache_mngr_poll() * @see nl_cache_mngr_data_ready() * * @return 0 on success or a negative error code. * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and * cache type * @return -NLE_OPNOTSUPP Cache type does not support updates * @return -NLE_EXIST Cache of this type already being managed */ int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache, change_func_t cb, void *data) { struct nl_cache_ops *ops; struct nl_af_group *grp; int err, i; ops = cache->c_ops; if (!ops) return -NLE_INVAL; if (ops->co_protocol != mngr->cm_protocol) return -NLE_PROTO_MISMATCH; if (ops->co_groups == NULL) return -NLE_OPNOTSUPP; for (i = 0; i < mngr->cm_nassocs; i++) if (mngr->cm_assocs[i].ca_cache && mngr->cm_assocs[i].ca_cache->c_ops == ops) return -NLE_EXIST; retry: for (i = 0; i < mngr->cm_nassocs; i++) if (!mngr->cm_assocs[i].ca_cache) break; if (i >= mngr->cm_nassocs) { mngr->cm_nassocs += NASSOC_EXPAND; mngr->cm_assocs = realloc(mngr->cm_assocs, mngr->cm_nassocs * sizeof(struct nl_cache_assoc)); if (mngr->cm_assocs == NULL) return -NLE_NOMEM; memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0, NASSOC_EXPAND * sizeof(struct nl_cache_assoc)); NL_DBG(1, "Increased capacity of cache manager %p " \ "to %d\n", mngr, mngr->cm_nassocs); goto retry; } for (grp = ops->co_groups; grp->ag_group; grp++) { err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group); if (err < 0) return err; } err = nl_cache_refill(mngr->cm_sync_sock, cache); if (err < 0) goto errout_drop_membership; mngr->cm_assocs[i].ca_cache = cache; mngr->cm_assocs[i].ca_change = cb; mngr->cm_assocs[i].ca_change_data = data; if (mngr->cm_flags & NL_AUTO_PROVIDE) nl_cache_mngt_provide(cache); NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", cache, nl_cache_name(cache), mngr); return 0; errout_drop_membership: for (grp = ops->co_groups; grp->ag_group; grp++) nl_socket_drop_membership(mngr->cm_sock, grp->ag_group); return err; }
int main(int argc, char **argv) { int c; int i; int family; int group; struct nl_sock *nl; struct nl_msg *msg; char *dummy = NULL; /* Currently processed command info */ struct iz_cmd cmd; /* Parse options */ while (1) { #ifdef HAVE_GETOPT_LONG int opt_idx = -1; c = getopt_long(argc, argv, "d::vh", iz_long_opts, &opt_idx); #else c = getopt(argc, argv, "d::vh"); #endif if (c == -1) break; switch(c) { case 'd': if (optarg) { i = strtol(optarg, &dummy, 10); if (*dummy == '\0') iz_debug = nl_debug = i; else { fprintf(stderr, "Error: incorrect debug level: '%s'\n", optarg); exit(1); } } else iz_debug = nl_debug = 1; break; case 'v': printf( "iz " VERSION "\n" "Copyright (C) 2008, 2009 by Siemens AG\n" "License GPLv2 GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n" "Written by Dmitry Eremin-Solenikov, Sergey Lapin and Maxim Osipov\n"); return 0; case 'h': iz_help(argv[0]); return 0; default: iz_help(argv[0]); return 1; } } if (optind >= argc) { iz_help(argv[0]); return 1; } memset(&cmd, 0, sizeof(cmd)); cmd.argc = argc - optind; cmd.argv = argv + optind; /* Parse command */ cmd.desc = get_cmd(argv[optind]); if (!cmd.desc) { printf("Unknown command %s!\n", argv[optind]); return 1; } if (cmd.desc->parse) { i = cmd.desc->parse(&cmd); if (i == IZ_STOP_OK) { return 0; } else if (i == IZ_STOP_ERR) { printf("Command line parsing error!\n"); return 1; } } /* Prepare NL command */ nl = nl_socket_alloc(); if (!nl) { nl_perror(NLE_NOMEM, "Could not allocate NL handle"); return 1; } genl_connect(nl); family = genl_ctrl_resolve(nl, IEEE802154_NL_NAME); group = nl_get_multicast_id(nl, IEEE802154_NL_NAME, IEEE802154_MCAST_COORD_NAME); if (group < 0) { fprintf(stderr, "Could not get multicast group ID: %s\n", strerror(-group)); return 1; } nl_socket_add_membership(nl, group); iz_seq = nl_socket_use_seq(nl) + 1; nl_socket_modify_cb(nl, NL_CB_VALID, NL_CB_CUSTOM, iz_cb_valid, (void*)&cmd); nl_socket_modify_cb(nl, NL_CB_FINISH, NL_CB_CUSTOM, iz_cb_finish, (void*)&cmd); nl_socket_modify_cb(nl, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, iz_cb_seq_check, (void*)&cmd); /* Send request, if necessary */ if (cmd.desc->request) { msg = nlmsg_alloc(); if (!msg) { nl_perror(NLE_NOMEM, "Could not allocate NL message!\n"); /* FIXME: err */ return 1; } genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, cmd.flags, cmd.desc->nl_cmd, 1); if (cmd.desc->request(&cmd, msg) != IZ_CONT_OK) { printf("Request processing error!\n"); return 1; } dprintf(1, "nl_send_auto_complete\n"); nl_send_auto_complete(nl, msg); cmd.seq = nlmsg_hdr(msg)->nlmsg_seq; dprintf(1, "nlmsg_free\n"); nlmsg_free(msg); } /* Received message handling loop */ while (iz_exit == IZ_CONT_OK) { int err = nl_recvmsgs_default(nl); if (err != NLE_SUCCESS) { nl_perror(err, "Receive failed"); return 1; } } nl_close(nl); if (iz_exit == IZ_STOP_ERR) return 1; return 0; }
/** * Add cache responsibility to cache manager * @arg mngr Cache manager. * @arg name Name of cache to keep track of * @arg cb Function to be called upon changes. * @arg data Argument passed on to change callback * @arg result Pointer to store added cache. * * Allocates a new cache of the specified type and adds it to the manager. * The operation will trigger a full dump request from the kernel to * initially fill the contents of the cache. The manager will subscribe * to the notification group of the cache to keep track of any further * changes. * * @return 0 on success or a negative error code. */ int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, change_func_t cb, void *data, struct nl_cache **result) { struct nl_cache_ops *ops; struct nl_cache *cache; struct nl_af_group *grp; int err, i; ops = nl_cache_ops_lookup(name); if (!ops) return -NLE_NOCACHE; if (ops->co_protocol != mngr->cm_protocol) return -NLE_PROTO_MISMATCH; if (ops->co_groups == NULL) return -NLE_OPNOTSUPP; for (i = 0; i < mngr->cm_nassocs; i++) if (mngr->cm_assocs[i].ca_cache && mngr->cm_assocs[i].ca_cache->c_ops == ops) return -NLE_EXIST; retry: for (i = 0; i < mngr->cm_nassocs; i++) if (!mngr->cm_assocs[i].ca_cache) break; if (i >= mngr->cm_nassocs) { mngr->cm_nassocs += 16; mngr->cm_assocs = realloc(mngr->cm_assocs, mngr->cm_nassocs * sizeof(struct nl_cache_assoc)); if (mngr->cm_assocs == NULL) return -NLE_NOMEM; else { NL_DBG(1, "Increased capacity of cache manager %p " \ "to %d\n", mngr, mngr->cm_nassocs); goto retry; } } cache = nl_cache_alloc(ops); if (!cache) return -NLE_NOMEM; for (grp = ops->co_groups; grp->ag_group; grp++) { err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group); if (err < 0) goto errout_free_cache; } err = nl_cache_refill(mngr->cm_handle, cache); if (err < 0) goto errout_drop_membership; mngr->cm_assocs[i].ca_cache = cache; mngr->cm_assocs[i].ca_change = cb; mngr->cm_assocs[i].ca_change_data = data; if (mngr->cm_flags & NL_AUTO_PROVIDE) nl_cache_mngt_provide(cache); NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", cache, nl_cache_name(cache), mngr); *result = cache; return 0; errout_drop_membership: for (grp = ops->co_groups; grp->ag_group; grp++) nl_socket_drop_membership(mngr->cm_handle, grp->ag_group); errout_free_cache: nl_cache_free(cache); return err; }
/** * virNetlinkEventServiceStart: * * start a monitor to receive netlink messages for libvirtd. * This registers a netlink socket with the event interface. * * @protocol: netlink protocol * @groups: broadcast groups to join in * Returns -1 if the monitor cannot be registered, 0 upon success */ int virNetlinkEventServiceStart(unsigned int protocol, unsigned int groups) { virNetlinkEventSrvPrivatePtr srv; int fd; int ret = -1; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); return -EINVAL; } if (server[protocol]) return 0; VIR_INFO("starting netlink event service with protocol %d", protocol); if (VIR_ALLOC(srv) < 0) return -1; if (virMutexInit(&srv->lock) < 0) { VIR_FREE(srv); return -1; } virNetlinkEventServerLock(srv); /* Allocate a new socket and get fd */ if (!(srv->netlinknh = virNetlinkCreateSocket(protocol))) goto error_locked; fd = nl_socket_get_fd(srv->netlinknh); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); goto error_server; } if (groups && nl_socket_add_membership(srv->netlinknh, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); goto error_server; } if (nl_socket_set_nonblocking(srv->netlinknh)) { virReportSystemError(errno, "%s", _("cannot set netlink socket nonblocking")); goto error_server; } if ((srv->eventwatch = virEventAddHandle(fd, VIR_EVENT_HANDLE_READABLE, virNetlinkEventCallback, srv, NULL)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to add netlink event handle watch")); goto error_server; } srv->netlinkfd = fd; VIR_DEBUG("netlink event listener on fd: %i running", fd); ret = 0; server[protocol] = srv; error_server: if (ret < 0) { nl_close(srv->netlinknh); virNetlinkFree(srv->netlinknh); } error_locked: virNetlinkEventServerUnlock(srv); if (ret < 0) { virMutexDestroy(&srv->lock); VIR_FREE(srv); } return ret; }
/** * virNetlinkCommand: * @nlmsg: pointer to netlink message * @respbuf: pointer to pointer where response buffer will be allocated * @respbuflen: pointer to integer holding the size of the response buffer * on return of the function. * @src_pid: the pid of the process to send a message * @dst_pid: the pid of the process to talk to, i.e., pid = 0 for kernel * @protocol: netlink protocol * @groups: the group identifier * * Send the given message to the netlink layer and receive response. * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ int virNetlinkCommand(struct nl_msg *nl_msg, struct nlmsghdr **resp, unsigned int *respbuflen, uint32_t src_pid, uint32_t dst_pid, unsigned int protocol, unsigned int groups) { int ret = -1; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = dst_pid, .nl_groups = 0, }; ssize_t nbytes; struct pollfd fds[1]; int fd; int n; struct nlmsghdr *nlmsg = nlmsg_hdr(nl_msg); virNetlinkHandle *nlhandle = NULL; int len = 0; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); goto cleanup; } if (!(nlhandle = virNetlinkCreateSocket(protocol))) goto cleanup; fd = nl_socket_get_fd(nlhandle); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); goto cleanup; } if (groups && nl_socket_add_membership(nlhandle, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); goto cleanup; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = src_pid ? src_pid : getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); goto cleanup; } memset(fds, 0, sizeof(fds)); fds[0].fd = fd; fds[0].events = POLLIN; n = poll(fds, ARRAY_CARDINALITY(fds), NETLINK_ACK_TIMEOUT_S); if (n <= 0) { if (n < 0) virReportSystemError(errno, "%s", _("error in poll call")); if (n == 0) virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); goto cleanup; } len = nl_recv(nlhandle, &nladdr, (unsigned char **)resp, NULL); if (len == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nl_recv failed - returned 0 bytes")); goto cleanup; } if (len < 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); goto cleanup; } ret = 0; *respbuflen = len; cleanup: if (ret < 0) { *resp = NULL; *respbuflen = 0; } virNetlinkFree(nlhandle); return ret; } /** * virNetlinkDumpLink: * * @ifname: The name of the interface; only use if ifindex <= 0 * @ifindex: The interface index; may be <= 0 if ifname is given * @data: Gets a pointer to the raw data from netlink. MUST BE FREED BY CALLER! * @nlattr: Pointer to a pointer of netlink attributes that will contain * the results * @src_pid: pid used for nl_pid of the local end of the netlink message * (0 == "use getpid()") * @dst_pid: pid of destination nl_pid if the kernel * is not the target of the netlink message but it is to be * sent to another process (0 if sending to the kernel) * * Get information from netlink about an interface given its name or index. * * Returns 0 on success, -1 on fatal error. */ int virNetlinkDumpLink(const char *ifname, int ifindex, void **nlData, struct nlattr **tb, uint32_t src_pid, uint32_t dst_pid) { int rc = -1; struct nlmsghdr *resp = NULL; struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC, .ifi_index = ifindex }; unsigned int recvbuflen; struct nl_msg *nl_msg; if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0) return -1; ifinfo.ifi_index = ifindex; nl_msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST); if (!nl_msg) { virReportOOMError(); return -1; } if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (ifname) { if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0) goto buffer_too_small; } # ifdef RTEXT_FILTER_VF /* if this filter exists in the kernel's netlink implementation, * we need to set it, otherwise the response message will not * contain the IFLA_VFINFO_LIST that we're looking for. */ { uint32_t ifla_ext_mask = RTEXT_FILTER_VF; if (nla_put(nl_msg, IFLA_EXT_MASK, sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) { goto buffer_too_small; } } # endif if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, src_pid, dst_pid, NETLINK_ROUTE, 0) < 0) goto cleanup; if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL) goto malformed_resp; switch (resp->nlmsg_type) { case NLMSG_ERROR: err = (struct nlmsgerr *)NLMSG_DATA(resp); if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) goto malformed_resp; if (err->error) { virReportSystemError(-err->error, _("error dumping %s (%d) interface"), ifname, ifindex); goto cleanup; } break; case GENL_ID_CTRL: case NLMSG_DONE: rc = nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL); if (rc < 0) goto malformed_resp; break; default: goto malformed_resp; } rc = 0; cleanup: nlmsg_free(nl_msg); if (rc < 0) VIR_FREE(resp); *nlData = resp; return rc; malformed_resp: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed netlink response message")); goto cleanup; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
static struct nl_addr *process_get_neigh_mac( struct get_neigh_handler *neigh_handler) { int err; struct nl_addr *ll_addr = get_neigh_mac(neigh_handler); struct rtnl_neigh *neigh_filter; fd_set fdset; int sock_fd; int fd; int nfds; int timer_fd; int ret; struct skt addr_dst; char buff[sizeof(SEND_PAYLOAD)] = SEND_PAYLOAD; int retries = 0; if (NULL != ll_addr) return ll_addr; err = nl_socket_add_membership(neigh_handler->sock, RTNLGRP_NEIGH); if (err < 0) return NULL; neigh_filter = create_filter_neigh_for_dst(neigh_handler->dst, neigh_handler->oif); if (neigh_filter == NULL) return NULL; set_neigh_filter(neigh_handler, neigh_filter); nl_socket_disable_seq_check(neigh_handler->sock); nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID, NL_CB_CUSTOM, &get_neigh_cb, neigh_handler); fd = nl_socket_get_fd(neigh_handler->sock); err = create_socket(neigh_handler, &addr_dst, &sock_fd); if (err) return NULL; err = try_send_to(sock_fd, buff, sizeof(buff), &addr_dst); if (err) goto close_socket; timer_fd = create_timer(neigh_handler); if (timer_fd < 0) goto close_socket; nfds = MAX(fd, timer_fd) + 1; while (1) { FD_ZERO(&fdset); FD_SET(fd, &fdset); FD_SET(timer_fd, &fdset); /* wait for an incoming message on the netlink socket */ ret = select(nfds, &fdset, NULL, NULL, NULL); if (ret == -1) { goto select_err; } else if (ret) { if (FD_ISSET(fd, &fdset)) { nl_recvmsgs_default(neigh_handler->sock); if (neigh_handler->found_ll_addr) break; } else { nl_cache_refill(neigh_handler->sock, neigh_handler->neigh_cache); ll_addr = get_neigh_mac(neigh_handler); if (NULL != ll_addr) { break; } else if (FD_ISSET(timer_fd, &fdset) && retries < NUM_OF_RETRIES) { try_send_to(sock_fd, buff, sizeof(buff), &addr_dst); } } if (FD_ISSET(timer_fd, &fdset)) { uint64_t read_val; ssize_t rc; rc = read(timer_fd, &read_val, sizeof(read_val)); assert(rc == sizeof(read_val)); if (++retries >= NUM_OF_TRIES) { if (!errno) errno = EDESTADDRREQ; break; } } } } select_err: close(timer_fd); close_socket: close(sock_fd); return ll_addr ? ll_addr : neigh_handler->found_ll_addr; }
/** * virNetlinkCommand: * @nlmsg: pointer to netlink message * @respbuf: pointer to pointer where response buffer will be allocated * @respbuflen: pointer to integer holding the size of the response buffer * on return of the function. * @src_pid: the pid of the process to send a message * @dst_pid: the pid of the process to talk to, i.e., pid = 0 for kernel * @protocol: netlink protocol * @groups: the group identifier * * Send the given message to the netlink layer and receive response. * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ int virNetlinkCommand(struct nl_msg *nl_msg, struct nlmsghdr **resp, unsigned int *respbuflen, uint32_t src_pid, uint32_t dst_pid, unsigned int protocol, unsigned int groups) { int rc = 0; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = dst_pid, .nl_groups = 0, }; ssize_t nbytes; struct pollfd fds[1]; int fd; int n; struct nlmsghdr *nlmsg = nlmsg_hdr(nl_msg); virNetlinkHandle *nlhandle = NULL; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); return -EINVAL; } nlhandle = virNetlinkAlloc(); if (!nlhandle) { virReportSystemError(errno, "%s", _("cannot allocate nlhandle for netlink")); return -1; } if (nl_connect(nlhandle, protocol) < 0) { virReportSystemError(errno, _("cannot connect to netlink socket with protocol %d"), protocol); rc = -1; goto error; } fd = nl_socket_get_fd(nlhandle); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); rc = -1; goto error; } if (groups && nl_socket_add_membership(nlhandle, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); rc = -1; goto error; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = src_pid ? src_pid : getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); rc = -1; goto error; } memset(fds, 0, sizeof(fds)); fds[0].fd = fd; fds[0].events = POLLIN; n = poll(fds, ARRAY_CARDINALITY(fds), NETLINK_ACK_TIMEOUT_S); if (n <= 0) { if (n < 0) virReportSystemError(errno, "%s", _("error in poll call")); if (n == 0) virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); rc = -1; goto error; } *respbuflen = nl_recv(nlhandle, &nladdr, (unsigned char **)resp, NULL); if (*respbuflen <= 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); rc = -1; } error: if (rc == -1) { VIR_FREE(*resp); *resp = NULL; *respbuflen = 0; } virNetlinkFree(nlhandle); return rc; } static void virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver) { virMutexLock(&driver->lock); }
static void obj_input(struct nl_object *obj, void *arg) { struct nl_dump_params dp = { .dp_type = NL_DUMP_STATS, .dp_fd = stdout, .dp_dump_msgtype = 1, }; nl_object_dump(obj, &dp); } static int event_input(struct nl_msg *msg, void *arg) { if (nl_msg_parse(msg, &obj_input, NULL) < 0) fprintf(stderr, "<<EVENT>> Unknown message type\n"); /* Exit nl_recvmsgs_def() and return to the main select() */ return NL_STOP; } int main(int argc, char *argv[]) { struct nl_handle *nlh; int err = 1; int i, idx; static const struct { enum nfnetlink_groups gr_id; const char* gr_name; } known_groups[] = { { NFNLGRP_CONNTRACK_NEW, "ct-new" }, { NFNLGRP_CONNTRACK_UPDATE, "ct-update" }, { NFNLGRP_CONNTRACK_DESTROY, "ct-destroy" }, { NFNLGRP_NONE, NULL } }; if (nltool_init(argc, argv) < 0) return -1; nlh = nltool_alloc_handle(); if (nlh == NULL) return -1; nl_disable_sequence_check(nlh); nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); if (argc > 1 && !strcasecmp(argv[1], "-h")) { printf("Usage: nf-monitor [<groups>]\n"); printf("Known groups:"); for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++) printf(" %s", known_groups[i].gr_name); printf("\n"); return 2; } if (nfnl_connect(nlh) < 0) { fprintf(stderr, "%s\n", nl_geterror()); goto errout; } for (idx = 1; argc > idx; idx++) { for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++) { if (!strcmp(argv[idx], known_groups[i].gr_name)) { if (nl_socket_add_membership(nlh, known_groups[i].gr_id) < 0) { fprintf(stderr, "%s: %s\n", argv[idx], nl_geterror()); goto errout; } break; } } if (known_groups[i].gr_id == NFNLGRP_NONE) fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]); } while (1) { fd_set rfds; int fd, retval; fd = nl_socket_get_fd(nlh); FD_ZERO(&rfds); FD_SET(fd, &rfds); /* wait for an incoming message on the netlink socket */ retval = select(fd+1, &rfds, NULL, NULL, NULL); if (retval) { /* FD_ISSET(fd, &rfds) will be true */ nl_recvmsgs_default(nlh); } } nl_close(nlh); errout: return err; }
/* Create a socket to netlink interface_t */ static int netlink_socket(nl_handle_t *nl, int flags, int group, ...) { int ret; va_list gp; memset(nl, 0, sizeof (*nl)); #ifdef _HAVE_LIBNL3_ /* We need to keep libnl3 in step with our netlink socket creation. */ nl->sk = nl_socket_alloc(); if ( nl->sk == NULL ) { log_message(LOG_INFO, "Netlink: Cannot allocate netlink socket" ); return -1; } ret = nl_connect(nl->sk, NETLINK_ROUTE); if (ret != 0) { log_message(LOG_INFO, "Netlink: Cannot open netlink socket : (%d)", ret); return -1; } /* Unfortunately we can't call nl_socket_add_memberships() with variadic arguments * from a variadic argument list passed to us */ va_start(gp, group); while (group != 0) { if (group < 0) { va_end(gp); return -1; } if ((ret = nl_socket_add_membership(nl->sk, group))) { log_message(LOG_INFO, "Netlink: Cannot add socket membership 0x%x : (%d)", group, ret); return -1; } group = va_arg(gp,int); } va_end(gp); if (flags & SOCK_NONBLOCK) { if ((ret = nl_socket_set_nonblocking(nl->sk))) { log_message(LOG_INFO, "Netlink: Cannot set netlink socket non-blocking : (%d)", ret); return -1; } } if ((ret = nl_socket_set_buffer_size(nl->sk, IF_DEFAULT_BUFSIZE, 0))) { log_message(LOG_INFO, "Netlink: Cannot set netlink buffer size : (%d)", ret); return -1; } nl->nl_pid = nl_socket_get_local_port(nl->sk); nl->fd = nl_socket_get_fd(nl->sk); /* Set CLOEXEC */ fcntl(nl->fd, F_SETFD, fcntl(nl->fd, F_GETFD) | FD_CLOEXEC); #else socklen_t addr_len; struct sockaddr_nl snl; #if !HAVE_DECL_SOCK_NONBLOCK int sock_flags = flags; flags &= ~SOCK_NONBLOCK; #endif nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | flags, NETLINK_ROUTE); if (nl->fd < 0) { log_message(LOG_INFO, "Netlink: Cannot open netlink socket : (%s)", strerror(errno)); return -1; } #if !HAVE_DECL_SOCK_NONBLOCK if ((sock_flags & SOCK_NONBLOCK) && set_sock_flags(nl->fd, F_SETFL, O_NONBLOCK)) return -1; #endif memset(&snl, 0, sizeof (snl)); snl.nl_family = AF_NETLINK; ret = bind(nl->fd, (struct sockaddr *) &snl, sizeof (snl)); if (ret < 0) { log_message(LOG_INFO, "Netlink: Cannot bind netlink socket : (%s)", strerror(errno)); close(nl->fd); return -1; } /* Join the requested groups */ va_start(gp, group); while (group != 0) { if (group < 0) { va_end(gp); return -1; } ret = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); if (ret < 0) { log_message(LOG_INFO, "Netlink: Cannot add membership on netlink socket : (%s)", strerror(errno)); va_end(gp); return -1; } group = va_arg(gp,int); } va_end(gp); addr_len = sizeof (snl); ret = getsockname(nl->fd, (struct sockaddr *) &snl, &addr_len); if (ret < 0 || addr_len != sizeof (snl)) { log_message(LOG_INFO, "Netlink: Cannot getsockname : (%s)", strerror(errno)); close(nl->fd); return -1; } if (snl.nl_family != AF_NETLINK) { log_message(LOG_INFO, "Netlink: Wrong address family %d", snl.nl_family); close(nl->fd); return -1; } /* Save the port id for checking message source later */ nl->nl_pid = snl.nl_pid; /* Set default rcvbuf size */ if_setsockopt_rcvbuf(&nl->fd, IF_DEFAULT_BUFSIZE); #endif nl->seq = (uint32_t)time(NULL); if (nl->fd < 0) return -1; return ret; }