Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 089c075d authored by Andreas Gruenbacher's avatar Andreas Gruenbacher Committed by Philipp Reisner
Browse files

drbd: Convert the generic netlink interface to accept connection endpoints

parent 44e52cfa
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -836,6 +836,11 @@ struct drbd_tconn { /* is a resource from the config file */
	wait_queue_head_t ping_wait;	/* Woken upon reception of a ping, and a state change */
	struct res_opts res_opts;

	struct sockaddr_storage my_addr;
	int my_addr_len;
	struct sockaddr_storage peer_addr;
	int peer_addr_len;

	struct drbd_socket data;	/* data/barrier/cstate/parameter packets */
	struct drbd_socket meta;	/* ping/ack (metadata) packets */
	int agreed_pro_version;		/* actually used protocol version */
@@ -1377,6 +1382,8 @@ extern void drbd_minor_destroy(struct kref *kref);
struct drbd_tconn *conn_create(const char *name);
extern void conn_destroy(struct kref *kref);
struct drbd_tconn *conn_get_by_name(const char *name);
extern struct drbd_tconn *conn_get_by_addrs(void *my_addr, int my_addr_len,
					    void *peer_addr, int peer_addr_len);
extern void conn_free_crypto(struct drbd_tconn *tconn);

extern int proc_details;
+21 −0
Original line number Diff line number Diff line
@@ -2420,6 +2420,27 @@ struct drbd_tconn *conn_get_by_name(const char *name)
	return tconn;
}

struct drbd_tconn *conn_get_by_addrs(void *my_addr, int my_addr_len,
				     void *peer_addr, int peer_addr_len)
{
	struct drbd_tconn *tconn;

	rcu_read_lock();
	list_for_each_entry_rcu(tconn, &drbd_tconns, all_tconn) {
		if (tconn->my_addr_len == my_addr_len &&
		    tconn->peer_addr_len == peer_addr_len &&
		    !memcmp(&tconn->my_addr, my_addr, my_addr_len) &&
		    !memcmp(&tconn->peer_addr, peer_addr, peer_addr_len)) {
			kref_get(&tconn->kref);
			goto found;
		}
	}
	tconn = NULL;
found:
	rcu_read_unlock();
	return tconn;
}

static int drbd_alloc_socket(struct drbd_socket *socket)
{
	socket->rbuf = (void *) __get_free_page(GFP_KERNEL);
+94 −64
Original line number Diff line number Diff line
@@ -94,6 +94,8 @@ static struct drbd_config_context {
	/* pointer into the request skb,
	 * limited lifetime! */
	char *resource_name;
	struct nlattr *my_addr;
	struct nlattr *peer_addr;

	/* reply buffer */
	struct sk_buff *reply_skb;
@@ -142,6 +144,7 @@ int drbd_msg_put_info(const char *info)
 */
#define DRBD_ADM_NEED_MINOR	1
#define DRBD_ADM_NEED_RESOURCE	2
#define DRBD_ADM_NEED_CONNECTION 4
static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
		unsigned flags)
{
@@ -174,6 +177,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
	adm_ctx.reply_dh->minor = d_in->minor;
	adm_ctx.reply_dh->ret_code = NO_ERROR;

	adm_ctx.volume = VOLUME_UNSPECIFIED;
	if (info->attrs[DRBD_NLA_CFG_CONTEXT]) {
		struct nlattr *nla;
		/* parse and validate only */
@@ -191,12 +195,21 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,

		/* and assign stuff to the global adm_ctx */
		nla = nested_attr_tb[__nla_type(T_ctx_volume)];
		adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED;
		if (nla)
			adm_ctx.volume = nla_get_u32(nla);
		nla = nested_attr_tb[__nla_type(T_ctx_resource_name)];
		if (nla)
			adm_ctx.resource_name = nla_data(nla);
	} else
		adm_ctx.volume = VOLUME_UNSPECIFIED;
		adm_ctx.my_addr = nested_attr_tb[__nla_type(T_ctx_my_addr)];
		adm_ctx.peer_addr = nested_attr_tb[__nla_type(T_ctx_peer_addr)];
		if ((adm_ctx.my_addr &&
		     nla_len(adm_ctx.my_addr) > sizeof(adm_ctx.tconn->my_addr)) ||
		    (adm_ctx.peer_addr &&
		     nla_len(adm_ctx.peer_addr) > sizeof(adm_ctx.tconn->peer_addr))) {
			err = -EINVAL;
			goto fail;
		}
	}

	adm_ctx.minor = d_in->minor;
	adm_ctx.mdev = minor_to_mdev(d_in->minor);
@@ -211,6 +224,26 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
		return ERR_INVALID_REQUEST;
	}

	if (flags & DRBD_ADM_NEED_CONNECTION) {
		if (adm_ctx.tconn && !(flags & DRBD_ADM_NEED_RESOURCE)) {
			drbd_msg_put_info("no resource name expected");
			return ERR_INVALID_REQUEST;
		}
		if (adm_ctx.mdev) {
			drbd_msg_put_info("no minor number expected");
			return ERR_INVALID_REQUEST;
		}
		if (adm_ctx.my_addr && adm_ctx.peer_addr)
			adm_ctx.tconn = conn_get_by_addrs(nla_data(adm_ctx.my_addr),
							  nla_len(adm_ctx.my_addr),
							  nla_data(adm_ctx.peer_addr),
							  nla_len(adm_ctx.peer_addr));
		if (!adm_ctx.tconn) {
			drbd_msg_put_info("unknown connection");
			return ERR_INVALID_REQUEST;
		}
	}

	/* some more paranoia, if the request was over-determined */
	if (adm_ctx.mdev && adm_ctx.tconn &&
	    adm_ctx.mdev->tconn != adm_ctx.tconn) {
@@ -268,31 +301,29 @@ static int drbd_adm_finish(struct genl_info *info, int retcode)
static void setup_khelper_env(struct drbd_tconn *tconn, char **envp)
{
	char *afs;
	struct net_conf *nc;

	rcu_read_lock();
	nc = rcu_dereference(tconn->net_conf);
	if (nc) {
		switch (((struct sockaddr *)nc->peer_addr)->sa_family) {
	/* FIXME: A future version will not allow this case. */
	if (tconn->my_addr_len == 0 || tconn->peer_addr_len == 0)
		return;

	switch (((struct sockaddr *)&tconn->peer_addr)->sa_family) {
	case AF_INET6:
		afs = "ipv6";
		snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI6",
				 &((struct sockaddr_in6 *)nc->peer_addr)->sin6_addr);
			 &((struct sockaddr_in6 *)&tconn->peer_addr)->sin6_addr);
		break;
	case AF_INET:
		afs = "ipv4";
		snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4",
				 &((struct sockaddr_in *)nc->peer_addr)->sin_addr);
			 &((struct sockaddr_in *)&tconn->peer_addr)->sin_addr);
		break;
	default:
		afs = "ssocks";
		snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4",
				 &((struct sockaddr_in *)nc->peer_addr)->sin_addr);
			 &((struct sockaddr_in *)&tconn->peer_addr)->sin_addr);
	}
	snprintf(envp[3], 20, "DRBD_PEER_AF=%s", afs);
}
	rcu_read_unlock();
}

int drbd_khelper(struct drbd_conf *mdev, char *cmd)
{
@@ -1874,7 +1905,7 @@ int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info)
	int rsr; /* re-sync running */
	struct crypto crypto = { };

	retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE);
	retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_CONNECTION);
	if (!adm_ctx.reply_skb)
		return retcode;
	if (retcode != NO_ERROR)
@@ -1986,18 +2017,39 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
	struct drbd_conf *mdev;
	struct net_conf *old_conf, *new_conf = NULL;
	struct crypto crypto = { };
	struct drbd_tconn *oconn;
	struct drbd_tconn *tconn;
	struct sockaddr *new_my_addr, *new_peer_addr, *taken_addr;
	enum drbd_ret_code retcode;
	int i;
	int err;

	retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE);

	if (!adm_ctx.reply_skb)
		return retcode;
	if (retcode != NO_ERROR)
		goto out;
	if (!(adm_ctx.my_addr && adm_ctx.peer_addr)) {
		drbd_msg_put_info("connection endpoint(s) missing");
		retcode = ERR_INVALID_REQUEST;
		goto out;
	}

	/* No need for _rcu here. All reconfiguration is
	 * strictly serialized on genl_lock(). We are protected against
	 * concurrent reconfiguration/addition/deletion */
	list_for_each_entry(tconn, &drbd_tconns, all_tconn) {
		if (nla_len(adm_ctx.my_addr) == tconn->my_addr_len &&
		    !memcmp(nla_data(adm_ctx.my_addr), &tconn->my_addr, tconn->my_addr_len)) {
			retcode = ERR_LOCAL_ADDR;
			goto out;
		}

		if (nla_len(adm_ctx.peer_addr) == tconn->peer_addr_len &&
		    !memcmp(nla_data(adm_ctx.peer_addr), &tconn->peer_addr, tconn->peer_addr_len)) {
			retcode = ERR_PEER_ADDR;
			goto out;
		}
	}

	tconn = adm_ctx.tconn;
	conn_reconfig_start(tconn);
@@ -2027,37 +2079,6 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
	if (retcode != NO_ERROR)
		goto fail;

	retcode = NO_ERROR;

	new_my_addr = (struct sockaddr *)&new_conf->my_addr;
	new_peer_addr = (struct sockaddr *)&new_conf->peer_addr;

	/* No need for _rcu here. All reconfiguration is
	 * strictly serialized on genl_lock(). We are protected against
	 * concurrent reconfiguration/addition/deletion */
	list_for_each_entry(oconn, &drbd_tconns, all_tconn) {
		struct net_conf *nc;
		if (oconn == tconn)
			continue;

		rcu_read_lock();
		nc = rcu_dereference(oconn->net_conf);
		if (nc) {
			taken_addr = (struct sockaddr *)&nc->my_addr;
			if (new_conf->my_addr_len == nc->my_addr_len &&
			    !memcmp(new_my_addr, taken_addr, new_conf->my_addr_len))
				retcode = ERR_LOCAL_ADDR;

			taken_addr = (struct sockaddr *)&nc->peer_addr;
			if (new_conf->peer_addr_len == nc->peer_addr_len &&
			    !memcmp(new_peer_addr, taken_addr, new_conf->peer_addr_len))
				retcode = ERR_PEER_ADDR;
		}
		rcu_read_unlock();
		if (retcode != NO_ERROR)
			goto fail;
	}

	retcode = alloc_crypto(&crypto, new_conf);
	if (retcode != NO_ERROR)
		goto fail;
@@ -2083,6 +2104,11 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
	tconn->csums_tfm = crypto.csums_tfm;
	tconn->verify_tfm = crypto.verify_tfm;

	tconn->my_addr_len = nla_len(adm_ctx.my_addr);
	memcpy(&tconn->my_addr, nla_data(adm_ctx.my_addr), tconn->my_addr_len);
	tconn->peer_addr_len = nla_len(adm_ctx.peer_addr);
	memcpy(&tconn->peer_addr, nla_data(adm_ctx.peer_addr), tconn->peer_addr_len);

	mutex_unlock(&tconn->conf_update);

	rcu_read_lock();
@@ -2170,7 +2196,7 @@ int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info)
	enum drbd_ret_code retcode;
	int err;

	retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE);
	retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_CONNECTION);
	if (!adm_ctx.reply_skb)
		return retcode;
	if (retcode != NO_ERROR)
@@ -2529,7 +2555,7 @@ int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info)
	return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
}

int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *resource_name, unsigned vnr)
int nla_put_drbd_cfg_context(struct sk_buff *skb, struct drbd_tconn *tconn, unsigned vnr)
{
	struct nlattr *nla;
	nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
@@ -2537,7 +2563,11 @@ int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *resource_name, uns
		goto nla_put_failure;
	if (vnr != VOLUME_UNSPECIFIED)
		NLA_PUT_U32(skb, T_ctx_volume, vnr);
	NLA_PUT_STRING(skb, T_ctx_resource_name, resource_name);
	NLA_PUT_STRING(skb, T_ctx_resource_name, tconn->name);
	if (tconn->my_addr_len)
		NLA_PUT(skb, T_ctx_my_addr, tconn->my_addr_len, &tconn->my_addr);
	if (tconn->peer_addr_len)
		NLA_PUT(skb, T_ctx_peer_addr, tconn->peer_addr_len, &tconn->peer_addr);
	nla_nest_end(skb, nla);
	return 0;

@@ -2574,7 +2604,7 @@ int nla_put_status_info(struct sk_buff *skb, struct drbd_conf *mdev,

	/* We need to add connection name and volume number information still.
	 * Minor number is in drbd_genlmsghdr. */
	if (nla_put_drbd_cfg_context(skb, mdev->tconn->name, mdev->vnr))
	if (nla_put_drbd_cfg_context(skb, mdev->tconn, mdev->vnr))
		goto nla_put_failure;

	if (res_opts_to_skb(skb, &mdev->tconn->res_opts, exclude_sensitive))
@@ -2736,7 +2766,7 @@ int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
			/* this is a tconn without a single volume */
			dh->minor = -1U;
			dh->ret_code = NO_ERROR;
			if (nla_put_drbd_cfg_context(skb, tconn->name, VOLUME_UNSPECIFIED))
			if (nla_put_drbd_cfg_context(skb, tconn, VOLUME_UNSPECIFIED))
				genlmsg_cancel(skb, dh);
			else
				genlmsg_end(skb, dh);
+9 −12
Original line number Diff line number Diff line
@@ -626,23 +626,21 @@ static struct socket *drbd_try_connect(struct drbd_tconn *tconn)
		rcu_read_unlock();
		return NULL;
	}

	sndbuf_size = nc->sndbuf_size;
	rcvbuf_size = nc->rcvbuf_size;
	connect_int = nc->connect_int;
	rcu_read_unlock();

	my_addr_len = min_t(int, nc->my_addr_len, sizeof(src_in6));
	memcpy(&src_in6, nc->my_addr, my_addr_len);
	my_addr_len = min_t(int, tconn->my_addr_len, sizeof(src_in6));
	memcpy(&src_in6, &tconn->my_addr, my_addr_len);

	if (((struct sockaddr *)nc->my_addr)->sa_family == AF_INET6)
	if (((struct sockaddr *)&tconn->my_addr)->sa_family == AF_INET6)
		src_in6.sin6_port = 0;
	else
		((struct sockaddr_in *)&src_in6)->sin_port = 0; /* AF_INET & AF_SCI */

	peer_addr_len = min_t(int, nc->peer_addr_len, sizeof(src_in6));
	memcpy(&peer_in6, nc->peer_addr, peer_addr_len);

	rcu_read_unlock();
	peer_addr_len = min_t(int, tconn->peer_addr_len, sizeof(src_in6));
	memcpy(&peer_in6, &tconn->peer_addr, peer_addr_len);

	what = "sock_create_kern";
	err = sock_create_kern(((struct sockaddr *)&src_in6)->sa_family,
@@ -714,15 +712,14 @@ static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn)
		rcu_read_unlock();
		return NULL;
	}

	sndbuf_size = nc->sndbuf_size;
	rcvbuf_size = nc->rcvbuf_size;
	connect_int = nc->connect_int;

	my_addr_len = min_t(int, nc->my_addr_len, sizeof(struct sockaddr_in6));
	memcpy(&my_addr, nc->my_addr, my_addr_len);
	rcu_read_unlock();

	my_addr_len = min_t(int, tconn->my_addr_len, sizeof(struct sockaddr_in6));
	memcpy(&my_addr, &tconn->my_addr, my_addr_len);

	what = "sock_create_kern";
	err = sock_create_kern(((struct sockaddr *)&my_addr)->sa_family,
		SOCK_STREAM, IPPROTO_TCP, &s_listen);
+2 −0
Original line number Diff line number Diff line
@@ -1418,6 +1418,8 @@ static int w_after_conn_state_ch(struct drbd_work *w, int unused)

		mutex_lock(&tconn->conf_update);
		old_conf = tconn->net_conf;
		tconn->my_addr_len = 0;
		tconn->peer_addr_len = 0;
		rcu_assign_pointer(tconn->net_conf, NULL);
		conn_free_crypto(tconn);
		mutex_unlock(&tconn->conf_update);
Loading