Exemple #1
0
/* Change state and parameters on port. */
int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
{
	struct ifinfomsg *ifm;
	struct nlattr *protinfo;
	struct nlattr *afspec;
	struct net_bridge_port *p;
	struct nlattr *tb[IFLA_BRPORT_MAX + 1];
	int err;

	ifm = nlmsg_data(nlh);

	protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
	afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
	if (!protinfo && !afspec)
		return 0;

	p = br_port_get_rtnl(dev);
	/* We want to accept dev as bridge itself if the AF_SPEC
	 * is set to see if someone is setting vlan info on the brigde
	 */
	if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec))
		return -EINVAL;

	if (p && protinfo) {
		if (protinfo->nla_type & NLA_F_NESTED) {
			err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
					       protinfo, ifla_brport_policy);
			if (err)
				return err;

			spin_lock_bh(&p->br->lock);
			err = br_setport(p, tb);
			spin_unlock_bh(&p->br->lock);
		} else {
			/* Binary compatability with old RSTP */
			if (nla_len(protinfo) < sizeof(u8))
				return -EINVAL;

			spin_lock_bh(&p->br->lock);
			err = br_set_port_state(p, nla_get_u8(protinfo));
			spin_unlock_bh(&p->br->lock);
		}
		if (err)
			goto out;
	}

	if (afspec) {
		err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
				afspec, RTM_SETLINK);
	}

	if (err == 0)
		br_ifinfo_notify(RTM_NEWLINK, p);

out:
	return err;
}
int handle_immigration_request(struct nl_msg *req_msg) {
	struct nl_msg *msg = NULL;
	struct nlattr *nla;
	int ret = 0;
	int seq;
	struct internal_state* state = get_current_state();

	// In params	
	int uid;
	int slot_index;
	char* name;
	// Out params
	int accept = 1;
	
	seq = nlmsg_hdr(req_msg)->nlmsg_seq;

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_UID);
	if (nla == NULL)
		return  -EBADMSG;
	uid = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_INDEX);
	if (nla == NULL)
		return  -EBADMSG;
	slot_index = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_NAME);
	if (nla == NULL)
		return  -EBADMSG;
	//name = nl_data_get(nla_get_data(nla));
	name = nla_data(nla);

	//printf("NPM CALLED FOR NAME: %s\n", name);
	if ( immigration_request_callback )
        	immigration_request_callback(uid, slot_index, name, &accept);
	
	if ( (ret=prepare_response_message(state->handle, DIRECTOR_IMMIGRATION_REQUEST_RESPONSE, state->gnl_fid, seq, &msg) ) != 0 ) {
		goto done;
	}
	
	ret = nla_put_u32(msg,
			DIRECTOR_A_DECISION,
			accept);

	if (ret != 0)
		goto error_del_resp;

	ret = send_request_message(state->handle, msg, 0);
	goto done;	

error_del_resp:
	nlmsg_free(msg);
done:	
	return ret;
}
Exemple #3
0
int handle_task_exitted(struct nl_msg *req_msg) {
	struct nl_msg *msg = NULL;
	struct nlattr *nla;
	int ret = 0;
	int seq;
	struct internal_state* state = get_current_state();
	struct rusage *rusage;

	// In params
	pid_t pid;
	int exit_code;
	
	seq = nlmsg_hdr(req_msg)->nlmsg_seq;

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_PID);
	if (nla == NULL)
		return  -EBADMSG;
	pid = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_EXIT_CODE);
	if (nla == NULL)
		return  -EBADMSG;
	exit_code = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_RUSAGE);
	if (nla == NULL)
		return  -EBADMSG;
	
	//rusage = nl_data_get(nla_get_data(nla));
	rusage = nla_data(nla);

	if ( task_exitted_callback )
        	task_exitted_callback(pid, exit_code, rusage);
	
	if ( (ret=prepare_response_message(state->sk, DIRECTOR_ACK, state->gnl_fid, seq, &msg) ) != 0 ) {
		goto done;
	}
	
	if (ret != 0)
		goto error_del_resp;

	ret = send_request_message(state->sk, msg, 0);
	goto done;	

error_del_resp:
	nlmsg_free(msg);
done:	
	return ret;
}
Exemple #4
0
static void
ind_ovs_handle_packet_miss(struct ind_ovs_upcall_thread *thread,
                           struct ind_ovs_port *port,
                           struct nl_msg *msg, struct nlattr **attrs)
{
    struct nlmsghdr *nlh = nlmsg_hdr(msg);
    struct genlmsghdr *gnlh = (void *)(nlh + 1);

    struct nlattr *key = attrs[OVS_PACKET_ATTR_KEY];
    struct nlattr *packet = attrs[OVS_PACKET_ATTR_PACKET];
    assert(key && packet);

    struct ind_ovs_parsed_key pkey;
    ind_ovs_parse_key(key, &pkey);

    /* Lookup the flow in the userspace flowtable. */
    struct ind_ovs_flow *flow;
    if (ind_ovs_lookup_flow(&pkey, &flow) != 0) {
        ind_ovs_upcall_request_pktin(pkey.in_port, port, packet, key, OF_PACKET_IN_REASON_NO_MATCH);
        return;
    }

    /* Reuse the incoming message for the packet execute */
    gnlh->cmd = OVS_PACKET_CMD_EXECUTE;
    ind_ovs_translate_actions(&pkey, flow->of_list_action,
                              msg, OVS_PACKET_ATTR_ACTIONS);

    __sync_fetch_and_add(&flow->packets, 1);
    __sync_fetch_and_add(&flow->bytes, nla_len(packet));

    /* Reuse the translated actions for adding the kflow. */
    struct nlattr *actions = nlmsg_find_attr(nlmsg_hdr(msg),
        sizeof(struct genlmsghdr) + sizeof(struct ovs_header),
        OVS_PACKET_ATTR_ACTIONS);

    /* Don't send the packet back out if it would be dropped. */
    if (nla_len(actions) > 0) {
        nlh->nlmsg_pid = 0;
        nlh->nlmsg_seq = 0;
        nlh->nlmsg_flags = NLM_F_REQUEST;
        struct iovec *iovec = &thread->tx_queue[thread->tx_queue_len++];
        iovec->iov_base = nlh;
        iovec->iov_len = nlh->nlmsg_len;
        if (thread->log_upcalls) {
            LOG_VERBOSE("Sending upcall reply:");
            ind_ovs_dump_msg(nlh);
        }
    }

    /* See the comment for ind_ovs_upcall_seen_key. */
    if (ind_ovs_upcall_seen_key(thread, key)) {
        /* Create a kflow with the given key and actions. */
        ind_ovs_bh_request_kflow(key, actions);
    }
}
Exemple #5
0
static int npm_check_read_response(struct genl_info* info, void* params) {
	int ret = 0;
	struct nlattr* attr;
	struct npm_check_params* check_params = params;

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_DECISION);
	if ( attr == NULL ) {
		ret = -EBADMSG;
		goto done;
	}

	check_params->decision = nla_get_u32(attr);

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_DECISION_VALUE);
	if ( attr != NULL ) {
		check_params->decision_value = nla_get_u32(attr);
	} else {
		check_params->decision_value = 0;
	}
done:
	return ret;
}
ssize_t wimax_msg_len(struct sk_buff *msg)
{
	struct nlmsghdr *nlh = (void *) msg->head;
	struct nlattr *nla;

	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
			      WIMAX_GNL_MSG_DATA);
	if (nla == NULL) {
		printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n");
		return -EINVAL;
	}
	return nla_len(nla);
}
Exemple #7
0
/*
 * Change state of port (ie from forwarding to blocking etc)
 * Used by spanning tree in user space.
 */
static int br_rtm_setlink(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
{
	struct net *net = sock_net(skb->sk);
	struct ifinfomsg *ifm;
	struct nlattr *protinfo;
	struct net_device *dev;
	struct net_bridge_port *p;
	u8 new_state;

	if (nlmsg_len(nlh) < sizeof(*ifm))
		return -EINVAL;

	ifm = nlmsg_data(nlh);
	if (ifm->ifi_family != AF_BRIDGE)
		return -EPFNOSUPPORT;

	protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
	if (!protinfo || nla_len(protinfo) < sizeof(u8))
		return -EINVAL;

	new_state = nla_get_u8(protinfo);
	if (new_state > BR_STATE_BLOCKING)
		return -EINVAL;

	dev = __dev_get_by_index(net, ifm->ifi_index);
	if (!dev)
		return -ENODEV;

	p = br_port_get_rtnl(dev);
	if (!p)
		return -EINVAL;

	/* if kernel STP is running, don't allow changes */
	if (p->br->stp_enabled == BR_KERNEL_STP)
		return -EBUSY;

	if (!netif_running(dev) ||
	    (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
		return -ENETDOWN;

	p->state = new_state;
	br_log_state(p);

	spin_lock_bh(&p->br->lock);
	br_port_state_selection(p->br);
	spin_unlock_bh(&p->br->lock);

	br_ifinfo_notify(RTM_NEWLINK, p);

	return 0;
}
int handle_send_generic_user_message(struct sk_buff *skb, struct genl_info *info) {
	struct nlattr* attr;
	char* user_data;
	int user_data_length, target_slot_index, target_slot_type;

	// No handler registered?
	if ( !generic_user_message_handler ) {
		return -EINVAL;
	}

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_SLOT_INDEX);
	if ( attr == NULL ) {
		return -1;
	}
	target_slot_index = nla_get_u32(attr);

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_SLOT_TYPE);
	if ( attr == NULL ) {
		return -1;
	}
	target_slot_type = nla_get_u32(attr);

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_LENGTH);
	if ( attr == NULL ) {
		return -1;
	}
	user_data_length = nla_get_u32(attr);

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_USER_DATA);
	if ( attr == NULL ) {
		return -1;
	}
	user_data = nla_data(attr);

	return generic_user_message_handler(target_slot_type, target_slot_index, user_data_length, user_data);
};
static int node_connected_read_response(struct genl_info* info, void* params) {
	int ret = 0;
	struct nlattr* attr;
	struct node_connected_params* node_connected_params = params;

	attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_DECISION);
	if ( attr == NULL ) {
		ret = -EBADMSG;
		goto done;
	}

	node_connected_params->accepted = nla_get_u32(attr);
done:
	return ret;
}
Exemple #10
0
/*
 * Change state of port (ie from forwarding to blocking etc)
 * Used by spanning tree in user space.
 */
static int br_rtm_setlink(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
{
	struct ifinfomsg *ifm;
	struct nlattr *protinfo;
	struct net_device *dev;
	struct net_bridge_port *p;
	u8 new_state;

	if (nlmsg_len(nlh) < sizeof(*ifm))
		return -EINVAL;

	ifm = nlmsg_data(nlh);
	if (ifm->ifi_family != AF_BRIDGE)
		return -EPFNOSUPPORT;

	protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
	if (!protinfo || nla_len(protinfo) < sizeof(u8))
		return -EINVAL;

	new_state = nla_get_u8(protinfo);
	if (new_state > BR_STATE_BLOCKING)
		return -EINVAL;

	dev = __if_dev_get_by_index(ifm->ifi_index);
	if (!dev)
		return -ENODEV;

	p = dev->br_port;
	if (!p)
		return -EINVAL;

	/* if kernel STP is running, don't allow changes */
	if (p->br->stp_enabled)
		return -EBUSY;

	if (!if_dev_admin_up(dev) ||
	    (!if_dev_running(dev) && new_state != BR_STATE_DISABLED))
		return -ENETDOWN;

	p->state = new_state;
	br_log_state(p);
	return 0;
}
/* Change state and parameters on port. */
int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
{
	struct ifinfomsg *ifm;
	struct nlattr *protinfo;
	struct net_bridge_port *p;
	struct nlattr *tb[IFLA_BRPORT_MAX + 1];
	int err;

	ifm = nlmsg_data(nlh);

	protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
	if (!protinfo)
		return 0;

	p = br_port_get_rtnl(dev);
	if (!p)
		return -EINVAL;

	if (protinfo->nla_type & NLA_F_NESTED) {
		err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
				       protinfo, ifla_brport_policy);
		if (err)
			return err;

		spin_lock_bh(&p->br->lock);
		err = br_setport(p, tb);
		spin_unlock_bh(&p->br->lock);
	} else {
		/* Binary compatability with old RSTP */
		if (nla_len(protinfo) < sizeof(u8))
			return -EINVAL;

		spin_lock_bh(&p->br->lock);
		err = br_set_port_state(p, nla_get_u8(protinfo));
		spin_unlock_bh(&p->br->lock);
	}

	if (err == 0)
		br_ifinfo_notify(RTM_NEWLINK, p);

	return err;
}
Exemple #12
0
/*
 * See the OF_PORT_DEST_USE_TABLE comment in ind_ovs_translate_actions.
 * This is mostly the same as ind_ovs_handle_packet_miss but does
 * not reuse the original message for the execute command since
 * the incoming message has a userdata attribute.
 * It also doesn't install a kflow or update flow stats.
 */
static void
ind_ovs_handle_packet_table(struct ind_ovs_upcall_thread *thread,
                            struct ind_ovs_port *port,
                            struct nl_msg *msg, struct nlattr **attrs)
{
    struct nlattr *key = attrs[OVS_PACKET_ATTR_KEY];
    struct nlattr *packet = attrs[OVS_PACKET_ATTR_PACKET];
    assert(key && packet);

    struct ind_ovs_parsed_key pkey;
    ind_ovs_parse_key(key, &pkey);

    /* Lookup the flow in the userspace flowtable. */
    struct ind_ovs_flow *flow;
    if (ind_ovs_lookup_flow(&pkey, &flow) != 0) {
        ind_ovs_upcall_request_pktin(pkey.in_port, port, packet, key, OF_PACKET_IN_REASON_NO_MATCH);
        return;
    }

    /* Send OVS_PACKET_CMD_EXECUTE. */
    /* TODO ensure the message is large enough */
    struct nl_msg *reply = ind_ovs_create_nlmsg(ovs_packet_family,
                                                OVS_PACKET_CMD_EXECUTE);

    ind_ovs_translate_actions(&pkey, flow->of_list_action,
                              reply, OVS_PACKET_ATTR_ACTIONS);

    nla_put(reply, OVS_PACKET_ATTR_KEY, nla_len(key), nla_data(key));
    nla_put(reply, OVS_PACKET_ATTR_PACKET, nla_len(packet), nla_data(packet));

    struct nlattr *actions = nlmsg_find_attr(nlmsg_hdr(reply),
        sizeof(struct genlmsghdr) + sizeof(struct ovs_header),
        OVS_PACKET_ATTR_ACTIONS);

    /* Don't send the packet back out if it would be dropped. */
    if (nla_len(actions) > 0) {
        nl_send_auto(port->notify_socket, reply);
    }

    ind_ovs_nlmsg_freelist_free(reply);
}
Exemple #13
0
/* Delete port information */
int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
{
	struct ifinfomsg *ifm;
	struct nlattr *afspec;
	struct net_bridge_port *p;
	int err;

	ifm = nlmsg_data(nlh);

	afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
	if (!afspec)
		return 0;

	p = br_port_get_rtnl(dev);
	/* We want to accept dev as bridge itself as well */
	if (!p && !(dev->priv_flags & IFF_EBRIDGE))
		return -EINVAL;

	err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
			afspec, RTM_DELLINK);

	return err;
}
Exemple #14
0
/*
 * Process DELLINK event
 */
int
__ni_rtevent_dellink(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct ifinfomsg *ifi;
	ni_netdev_t *dev;
	struct nlattr *nla;
	const char *ifname = NULL;

	if (!(ifi = ni_rtnl_ifinfomsg(h, RTM_DELLINK)))
		return -1;

	if ((nla = nlmsg_find_attr(h, sizeof(*ifi), IFLA_IFNAME)) != NULL) {
		ifname = (char *) nla_data(nla);
	}
	if (ifi->ifi_family == AF_BRIDGE) {
		ni_debug_events("%s: ignoring bridge DELLINK event", ifname);
		return 0;
	}

	/* Open code interface removal. */
	if ((dev = ni_netdev_by_index(nc, ifi->ifi_index)) == NULL) {
		ni_debug_events("RTM_DELLINK message for unknown interface %s index %d",
				ifname, ifi->ifi_index);
		return -1;
	} else {
		unsigned int old_flags = dev->link.ifflags;

		dev->link.ifflags = __ni_netdev_translate_ifflags(ifi->ifi_flags, old_flags);
		dev->deleted = 1;
		__ni_netdev_process_events(nc, dev, old_flags);
		ni_client_state_drop(dev->link.ifindex);
		ni_netconfig_device_remove(nc, dev);
	}

	return 0;
}
Exemple #15
0
/** 
 * Setups generic netlink connection with the kernel module and retrieves assocaited family id
 *
 * @return 0 on success
 */
static int initialize_netlink_family(void) {
	struct nl_handle* hndl;
	struct nl_msg* msg = NULL;
	struct nl_msg *ans_msg = NULL;
	struct nlmsghdr *nl_hdr;
	struct genlmsghdr* genl_hdr;
	struct nlattr *nla;
	int ret_val = 0;

	hndl = nl_handle_alloc();
	nl_set_buffer_size(hndl, 15000000, 15000000);
	
	//nl_handle_set_peer_pid(hndl, 0);
	//nl_set_passcred(hndl, 1);
	nl_disable_sequence_check(hndl);

	if ( (ret_val=nl_connect(hndl, NETLINK_GENERIC)) )
		goto init_return;
	
	nl_set_buffer_size(hndl, 15000000, 15000000);
  
	if ( (ret_val=prepare_request_message(hndl, CTRL_CMD_GETFAMILY, GENL_ID_CTRL, &msg) ) != 0 ) {
		goto init_return;
  	}
  
	ret_val = nla_put_string(msg,
		   CTRL_ATTR_FAMILY_NAME,
		   "DIRECTORCHNL");

  	if (ret_val != 0)
		goto init_return;

	if ( (ret_val = send_request_message(hndl, msg, 0) ) != 0 )
		goto init_return;
	if ( (ret_val = read_message(hndl, &ans_msg) ) != 0 )
		goto init_return;

	genl_hdr = nl_msg_genlhdr(ans_msg);
 	if (genl_hdr == NULL || genl_hdr->cmd != CTRL_CMD_NEWFAMILY) {
    		ret_val = -EBADMSG;
    		goto init_return;
  	}

	nla = nlmsg_find_attr(nlmsg_hdr(ans_msg), sizeof(struct genlmsghdr), CTRL_ATTR_FAMILY_ID);
  	if (nla == NULL) {
    		ret_val = -EBADMSG;
    		goto init_return;
  	}

  	state.gnl_fid = nla_get_u16(nla);  
  	if (state.gnl_fid == 0) {
    		ret_val = -EBADMSG;
    		goto init_return;
  	}
  	  	

  	state.handle = hndl;

	return 0;

init_return:
	nlmsg_free(ans_msg);
	
	if ( state.handle == NULL ) {
		nl_close(hndl);
		nl_handle_destroy(hndl);
  	}

	return -EINVAL;
}
int handle_immigration_confirmed(struct nl_msg *req_msg) {
	struct nl_msg *msg = NULL;
	struct nlattr *nla;
	int ret = 0;
	int seq;
	struct internal_state* state = get_current_state();

	// In params	
	int uid;
	int slot_index;
	char* name;
	pid_t local_pid;
	pid_t remote_pid;
	unsigned long jiffies;
	
	seq = nlmsg_hdr(req_msg)->nlmsg_seq;

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_UID);
	if (nla == NULL)
		return  -EBADMSG;
	uid = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_INDEX);
	if (nla == NULL)
		return  -EBADMSG;
	slot_index = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_NAME);
	if (nla == NULL)
		return  -EBADMSG;
	//name = nl_data_get(nla_get_data(nla));
	name = nla_data(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_JIFFIES);
	if (nla == NULL)
		return  -EBADMSG;
	jiffies = nla_get_u64(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_PID);
	if (nla == NULL)
		return  -EBADMSG;
	local_pid = nla_get_u32(nla);

	nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_REMOTE_PID);
	if (nla == NULL)
		return  -EBADMSG;
	remote_pid = nla_get_u32(nla);

	//printf("NPM CALLED FOR NAME: %s\n", name);
	if ( immigration_confirmed_callback )
        	immigration_confirmed_callback(uid, slot_index, name, jiffies, local_pid, remote_pid);
	
	if ( (ret=prepare_response_message(state->sk, DIRECTOR_ACK, state->gnl_fid, seq, &msg) ) != 0 ) {
		goto done;
	}
	
	ret = send_request_message(state->sk, msg, 0);
	goto done;	

error_del_resp:
	nlmsg_free(msg);
done:	
	return ret;
}
Exemple #17
0
/*
 * Process NEWLINK event
 */
int
__ni_rtevent_newlink(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	char namebuf[IF_NAMESIZE+1] = {'\0'};
	ni_netdev_t *dev, *old;
	struct ifinfomsg *ifi;
	struct nlattr *nla;
	char *ifname = NULL;
	int old_flags = 0;

	if (!(ifi = ni_rtnl_ifinfomsg(h, RTM_NEWLINK)))
		return -1;

	if (ifi->ifi_family == AF_BRIDGE)
		return 0;

	old = ni_netdev_by_index(nc, ifi->ifi_index);
	ifname = if_indextoname(ifi->ifi_index, namebuf);
	if (!ifname) {
		/*
		 * device (index) does not exists any more;
		 * process deletion/cleanup of the device.
		 */
		if (old) {
			old_flags = old->link.ifflags;
			old->link.ifflags = 0;
			old->deleted = 1;

			__ni_netdev_process_events(nc, old, old_flags);
			ni_client_state_drop(old->link.ifindex);
			ni_netconfig_device_remove(nc, old);
		}
		return 0;
	}

	if (old) {
		if (!ni_string_eq(old->name, ifname)) {
			ni_debug_events("%s[%u]: device renamed to %s",
					old->name, old->link.ifindex, ifname);
			ni_string_dup(&old->name, ifname);
			__ni_netdev_event(nc, old, NI_EVENT_DEVICE_RENAME);
		}
		dev = old;
		old_flags = old->link.ifflags;
	} else {
		if (!(dev = ni_netdev_new(ifname, ifi->ifi_index))) {
			ni_warn("%s[%u]: unable to allocate memory for device",
					ifname, ifi->ifi_index);
			return -1;
		}
		dev->created = 1;
		ni_netconfig_device_append(nc, dev);
	}

	if (__ni_netdev_process_newlink(dev, h, ifi, nc) < 0) {
		ni_error("Problem parsing RTM_NEWLINK message for %s", ifname);
		return -1;
	}

	if ((ifname = dev->name)) {
		ni_netdev_t *conflict;

		conflict = ni_netdev_by_name(nc, ifname);
		if (conflict && conflict->link.ifindex != (unsigned int)ifi->ifi_index) {
			/*
			 * As the events often provide an already obsolete name [2 events,
			 * we process 1st with next in read buffer], we are reading the
			 * current dev->name in advance (above).
			 *
			 * On a rename like eth0->rename1->eth1, eth1->rename2->eth0, the
			 * current dev->name is already eth1 at processing time of eth0
			 * to rename1 event. This sometimes causes that we find eth1 in
			 * our device list [eth1 -> rename2 event in the read buffer].
			 *
			 * Just update the name of the conflicting device in advance too
			 * and when the interface does not exist any more, emit events.
			 */
			char *current = if_indextoname(conflict->link.ifindex, namebuf);
			if (current) {
				ni_string_dup(&conflict->name, current);
				__ni_netdev_event(nc, conflict, NI_EVENT_DEVICE_RENAME);
			} else {
				unsigned int ifflags = conflict->link.ifflags;
				conflict->link.ifflags = 0;
				conflict->deleted = 1;

				__ni_netdev_process_events(nc, conflict, ifflags);
				ni_client_state_drop(conflict->link.ifindex);
				ni_netconfig_device_remove(nc, conflict);
			}
		}
	}

	__ni_netdev_process_events(nc, dev, old_flags);

	if ((nla = nlmsg_find_attr(h, sizeof(*ifi), IFLA_WIRELESS)) != NULL)
		__ni_wireless_link_event(nc, dev, nla_data(nla), nla_len(nla));

	return 0;
}