diff options
author | Andreas Gruenbacher <agruen@linbit.com> | 2011-06-14 12:28:09 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2012-11-08 10:57:46 -0500 |
commit | 089c075d88ac9407b8d7c5c8fc4b21c0d940bd82 (patch) | |
tree | c68e1a24d7813abe883702255a37dc704f2ea8fd /drivers/block/drbd/drbd_nl.c | |
parent | 44e52cfaa22e44a0197b44cd72c3440bc2a6e1ed (diff) |
drbd: Convert the generic netlink interface to accept connection endpoints
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block/drbd/drbd_nl.c')
-rw-r--r-- | drivers/block/drbd/drbd_nl.c | 158 |
1 files changed, 94 insertions, 64 deletions
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 352be132b4be..e7933e04e7b8 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c | |||
@@ -94,6 +94,8 @@ static struct drbd_config_context { | |||
94 | /* pointer into the request skb, | 94 | /* pointer into the request skb, |
95 | * limited lifetime! */ | 95 | * limited lifetime! */ |
96 | char *resource_name; | 96 | char *resource_name; |
97 | struct nlattr *my_addr; | ||
98 | struct nlattr *peer_addr; | ||
97 | 99 | ||
98 | /* reply buffer */ | 100 | /* reply buffer */ |
99 | struct sk_buff *reply_skb; | 101 | struct sk_buff *reply_skb; |
@@ -142,6 +144,7 @@ int drbd_msg_put_info(const char *info) | |||
142 | */ | 144 | */ |
143 | #define DRBD_ADM_NEED_MINOR 1 | 145 | #define DRBD_ADM_NEED_MINOR 1 |
144 | #define DRBD_ADM_NEED_RESOURCE 2 | 146 | #define DRBD_ADM_NEED_RESOURCE 2 |
147 | #define DRBD_ADM_NEED_CONNECTION 4 | ||
145 | static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, | 148 | static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, |
146 | unsigned flags) | 149 | unsigned flags) |
147 | { | 150 | { |
@@ -174,6 +177,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, | |||
174 | adm_ctx.reply_dh->minor = d_in->minor; | 177 | adm_ctx.reply_dh->minor = d_in->minor; |
175 | adm_ctx.reply_dh->ret_code = NO_ERROR; | 178 | adm_ctx.reply_dh->ret_code = NO_ERROR; |
176 | 179 | ||
180 | adm_ctx.volume = VOLUME_UNSPECIFIED; | ||
177 | if (info->attrs[DRBD_NLA_CFG_CONTEXT]) { | 181 | if (info->attrs[DRBD_NLA_CFG_CONTEXT]) { |
178 | struct nlattr *nla; | 182 | struct nlattr *nla; |
179 | /* parse and validate only */ | 183 | /* parse and validate only */ |
@@ -191,12 +195,21 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, | |||
191 | 195 | ||
192 | /* and assign stuff to the global adm_ctx */ | 196 | /* and assign stuff to the global adm_ctx */ |
193 | nla = nested_attr_tb[__nla_type(T_ctx_volume)]; | 197 | nla = nested_attr_tb[__nla_type(T_ctx_volume)]; |
194 | adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED; | 198 | if (nla) |
199 | adm_ctx.volume = nla_get_u32(nla); | ||
195 | nla = nested_attr_tb[__nla_type(T_ctx_resource_name)]; | 200 | nla = nested_attr_tb[__nla_type(T_ctx_resource_name)]; |
196 | if (nla) | 201 | if (nla) |
197 | adm_ctx.resource_name = nla_data(nla); | 202 | adm_ctx.resource_name = nla_data(nla); |
198 | } else | 203 | adm_ctx.my_addr = nested_attr_tb[__nla_type(T_ctx_my_addr)]; |
199 | adm_ctx.volume = VOLUME_UNSPECIFIED; | 204 | adm_ctx.peer_addr = nested_attr_tb[__nla_type(T_ctx_peer_addr)]; |
205 | if ((adm_ctx.my_addr && | ||
206 | nla_len(adm_ctx.my_addr) > sizeof(adm_ctx.tconn->my_addr)) || | ||
207 | (adm_ctx.peer_addr && | ||
208 | nla_len(adm_ctx.peer_addr) > sizeof(adm_ctx.tconn->peer_addr))) { | ||
209 | err = -EINVAL; | ||
210 | goto fail; | ||
211 | } | ||
212 | } | ||
200 | 213 | ||
201 | adm_ctx.minor = d_in->minor; | 214 | adm_ctx.minor = d_in->minor; |
202 | adm_ctx.mdev = minor_to_mdev(d_in->minor); | 215 | 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, | |||
211 | return ERR_INVALID_REQUEST; | 224 | return ERR_INVALID_REQUEST; |
212 | } | 225 | } |
213 | 226 | ||
227 | if (flags & DRBD_ADM_NEED_CONNECTION) { | ||
228 | if (adm_ctx.tconn && !(flags & DRBD_ADM_NEED_RESOURCE)) { | ||
229 | drbd_msg_put_info("no resource name expected"); | ||
230 | return ERR_INVALID_REQUEST; | ||
231 | } | ||
232 | if (adm_ctx.mdev) { | ||
233 | drbd_msg_put_info("no minor number expected"); | ||
234 | return ERR_INVALID_REQUEST; | ||
235 | } | ||
236 | if (adm_ctx.my_addr && adm_ctx.peer_addr) | ||
237 | adm_ctx.tconn = conn_get_by_addrs(nla_data(adm_ctx.my_addr), | ||
238 | nla_len(adm_ctx.my_addr), | ||
239 | nla_data(adm_ctx.peer_addr), | ||
240 | nla_len(adm_ctx.peer_addr)); | ||
241 | if (!adm_ctx.tconn) { | ||
242 | drbd_msg_put_info("unknown connection"); | ||
243 | return ERR_INVALID_REQUEST; | ||
244 | } | ||
245 | } | ||
246 | |||
214 | /* some more paranoia, if the request was over-determined */ | 247 | /* some more paranoia, if the request was over-determined */ |
215 | if (adm_ctx.mdev && adm_ctx.tconn && | 248 | if (adm_ctx.mdev && adm_ctx.tconn && |
216 | adm_ctx.mdev->tconn != adm_ctx.tconn) { | 249 | adm_ctx.mdev->tconn != adm_ctx.tconn) { |
@@ -268,30 +301,28 @@ static int drbd_adm_finish(struct genl_info *info, int retcode) | |||
268 | static void setup_khelper_env(struct drbd_tconn *tconn, char **envp) | 301 | static void setup_khelper_env(struct drbd_tconn *tconn, char **envp) |
269 | { | 302 | { |
270 | char *afs; | 303 | char *afs; |
271 | struct net_conf *nc; | ||
272 | 304 | ||
273 | rcu_read_lock(); | 305 | /* FIXME: A future version will not allow this case. */ |
274 | nc = rcu_dereference(tconn->net_conf); | 306 | if (tconn->my_addr_len == 0 || tconn->peer_addr_len == 0) |
275 | if (nc) { | 307 | return; |
276 | switch (((struct sockaddr *)nc->peer_addr)->sa_family) { | 308 | |
277 | case AF_INET6: | 309 | switch (((struct sockaddr *)&tconn->peer_addr)->sa_family) { |
278 | afs = "ipv6"; | 310 | case AF_INET6: |
279 | snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI6", | 311 | afs = "ipv6"; |
280 | &((struct sockaddr_in6 *)nc->peer_addr)->sin6_addr); | 312 | snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI6", |
281 | break; | 313 | &((struct sockaddr_in6 *)&tconn->peer_addr)->sin6_addr); |
282 | case AF_INET: | 314 | break; |
283 | afs = "ipv4"; | 315 | case AF_INET: |
284 | snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4", | 316 | afs = "ipv4"; |
285 | &((struct sockaddr_in *)nc->peer_addr)->sin_addr); | 317 | snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4", |
286 | break; | 318 | &((struct sockaddr_in *)&tconn->peer_addr)->sin_addr); |
287 | default: | 319 | break; |
288 | afs = "ssocks"; | 320 | default: |
289 | snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4", | 321 | afs = "ssocks"; |
290 | &((struct sockaddr_in *)nc->peer_addr)->sin_addr); | 322 | snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4", |
291 | } | 323 | &((struct sockaddr_in *)&tconn->peer_addr)->sin_addr); |
292 | snprintf(envp[3], 20, "DRBD_PEER_AF=%s", afs); | ||
293 | } | 324 | } |
294 | rcu_read_unlock(); | 325 | snprintf(envp[3], 20, "DRBD_PEER_AF=%s", afs); |
295 | } | 326 | } |
296 | 327 | ||
297 | int drbd_khelper(struct drbd_conf *mdev, char *cmd) | 328 | 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) | |||
1874 | int rsr; /* re-sync running */ | 1905 | int rsr; /* re-sync running */ |
1875 | struct crypto crypto = { }; | 1906 | struct crypto crypto = { }; |
1876 | 1907 | ||
1877 | retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE); | 1908 | retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_CONNECTION); |
1878 | if (!adm_ctx.reply_skb) | 1909 | if (!adm_ctx.reply_skb) |
1879 | return retcode; | 1910 | return retcode; |
1880 | if (retcode != NO_ERROR) | 1911 | if (retcode != NO_ERROR) |
@@ -1986,18 +2017,39 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info) | |||
1986 | struct drbd_conf *mdev; | 2017 | struct drbd_conf *mdev; |
1987 | struct net_conf *old_conf, *new_conf = NULL; | 2018 | struct net_conf *old_conf, *new_conf = NULL; |
1988 | struct crypto crypto = { }; | 2019 | struct crypto crypto = { }; |
1989 | struct drbd_tconn *oconn; | ||
1990 | struct drbd_tconn *tconn; | 2020 | struct drbd_tconn *tconn; |
1991 | struct sockaddr *new_my_addr, *new_peer_addr, *taken_addr; | ||
1992 | enum drbd_ret_code retcode; | 2021 | enum drbd_ret_code retcode; |
1993 | int i; | 2022 | int i; |
1994 | int err; | 2023 | int err; |
1995 | 2024 | ||
1996 | retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE); | 2025 | retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE); |
2026 | |||
1997 | if (!adm_ctx.reply_skb) | 2027 | if (!adm_ctx.reply_skb) |
1998 | return retcode; | 2028 | return retcode; |
1999 | if (retcode != NO_ERROR) | 2029 | if (retcode != NO_ERROR) |
2000 | goto out; | 2030 | goto out; |
2031 | if (!(adm_ctx.my_addr && adm_ctx.peer_addr)) { | ||
2032 | drbd_msg_put_info("connection endpoint(s) missing"); | ||
2033 | retcode = ERR_INVALID_REQUEST; | ||
2034 | goto out; | ||
2035 | } | ||
2036 | |||
2037 | /* No need for _rcu here. All reconfiguration is | ||
2038 | * strictly serialized on genl_lock(). We are protected against | ||
2039 | * concurrent reconfiguration/addition/deletion */ | ||
2040 | list_for_each_entry(tconn, &drbd_tconns, all_tconn) { | ||
2041 | if (nla_len(adm_ctx.my_addr) == tconn->my_addr_len && | ||
2042 | !memcmp(nla_data(adm_ctx.my_addr), &tconn->my_addr, tconn->my_addr_len)) { | ||
2043 | retcode = ERR_LOCAL_ADDR; | ||
2044 | goto out; | ||
2045 | } | ||
2046 | |||
2047 | if (nla_len(adm_ctx.peer_addr) == tconn->peer_addr_len && | ||
2048 | !memcmp(nla_data(adm_ctx.peer_addr), &tconn->peer_addr, tconn->peer_addr_len)) { | ||
2049 | retcode = ERR_PEER_ADDR; | ||
2050 | goto out; | ||
2051 | } | ||
2052 | } | ||
2001 | 2053 | ||
2002 | tconn = adm_ctx.tconn; | 2054 | tconn = adm_ctx.tconn; |
2003 | conn_reconfig_start(tconn); | 2055 | conn_reconfig_start(tconn); |
@@ -2027,37 +2079,6 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info) | |||
2027 | if (retcode != NO_ERROR) | 2079 | if (retcode != NO_ERROR) |
2028 | goto fail; | 2080 | goto fail; |
2029 | 2081 | ||
2030 | retcode = NO_ERROR; | ||
2031 | |||
2032 | new_my_addr = (struct sockaddr *)&new_conf->my_addr; | ||
2033 | new_peer_addr = (struct sockaddr *)&new_conf->peer_addr; | ||
2034 | |||
2035 | /* No need for _rcu here. All reconfiguration is | ||
2036 | * strictly serialized on genl_lock(). We are protected against | ||
2037 | * concurrent reconfiguration/addition/deletion */ | ||
2038 | list_for_each_entry(oconn, &drbd_tconns, all_tconn) { | ||
2039 | struct net_conf *nc; | ||
2040 | if (oconn == tconn) | ||
2041 | continue; | ||
2042 | |||
2043 | rcu_read_lock(); | ||
2044 | nc = rcu_dereference(oconn->net_conf); | ||
2045 | if (nc) { | ||
2046 | taken_addr = (struct sockaddr *)&nc->my_addr; | ||
2047 | if (new_conf->my_addr_len == nc->my_addr_len && | ||
2048 | !memcmp(new_my_addr, taken_addr, new_conf->my_addr_len)) | ||
2049 | retcode = ERR_LOCAL_ADDR; | ||
2050 | |||
2051 | taken_addr = (struct sockaddr *)&nc->peer_addr; | ||
2052 | if (new_conf->peer_addr_len == nc->peer_addr_len && | ||
2053 | !memcmp(new_peer_addr, taken_addr, new_conf->peer_addr_len)) | ||
2054 | retcode = ERR_PEER_ADDR; | ||
2055 | } | ||
2056 | rcu_read_unlock(); | ||
2057 | if (retcode != NO_ERROR) | ||
2058 | goto fail; | ||
2059 | } | ||
2060 | |||
2061 | retcode = alloc_crypto(&crypto, new_conf); | 2082 | retcode = alloc_crypto(&crypto, new_conf); |
2062 | if (retcode != NO_ERROR) | 2083 | if (retcode != NO_ERROR) |
2063 | goto fail; | 2084 | goto fail; |
@@ -2083,6 +2104,11 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info) | |||
2083 | tconn->csums_tfm = crypto.csums_tfm; | 2104 | tconn->csums_tfm = crypto.csums_tfm; |
2084 | tconn->verify_tfm = crypto.verify_tfm; | 2105 | tconn->verify_tfm = crypto.verify_tfm; |
2085 | 2106 | ||
2107 | tconn->my_addr_len = nla_len(adm_ctx.my_addr); | ||
2108 | memcpy(&tconn->my_addr, nla_data(adm_ctx.my_addr), tconn->my_addr_len); | ||
2109 | tconn->peer_addr_len = nla_len(adm_ctx.peer_addr); | ||
2110 | memcpy(&tconn->peer_addr, nla_data(adm_ctx.peer_addr), tconn->peer_addr_len); | ||
2111 | |||
2086 | mutex_unlock(&tconn->conf_update); | 2112 | mutex_unlock(&tconn->conf_update); |
2087 | 2113 | ||
2088 | rcu_read_lock(); | 2114 | rcu_read_lock(); |
@@ -2170,7 +2196,7 @@ int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info) | |||
2170 | enum drbd_ret_code retcode; | 2196 | enum drbd_ret_code retcode; |
2171 | int err; | 2197 | int err; |
2172 | 2198 | ||
2173 | retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE); | 2199 | retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_CONNECTION); |
2174 | if (!adm_ctx.reply_skb) | 2200 | if (!adm_ctx.reply_skb) |
2175 | return retcode; | 2201 | return retcode; |
2176 | if (retcode != NO_ERROR) | 2202 | if (retcode != NO_ERROR) |
@@ -2529,7 +2555,7 @@ int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info) | |||
2529 | return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED)); | 2555 | return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED)); |
2530 | } | 2556 | } |
2531 | 2557 | ||
2532 | int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *resource_name, unsigned vnr) | 2558 | int nla_put_drbd_cfg_context(struct sk_buff *skb, struct drbd_tconn *tconn, unsigned vnr) |
2533 | { | 2559 | { |
2534 | struct nlattr *nla; | 2560 | struct nlattr *nla; |
2535 | nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT); | 2561 | 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 | |||
2537 | goto nla_put_failure; | 2563 | goto nla_put_failure; |
2538 | if (vnr != VOLUME_UNSPECIFIED) | 2564 | if (vnr != VOLUME_UNSPECIFIED) |
2539 | NLA_PUT_U32(skb, T_ctx_volume, vnr); | 2565 | NLA_PUT_U32(skb, T_ctx_volume, vnr); |
2540 | NLA_PUT_STRING(skb, T_ctx_resource_name, resource_name); | 2566 | NLA_PUT_STRING(skb, T_ctx_resource_name, tconn->name); |
2567 | if (tconn->my_addr_len) | ||
2568 | NLA_PUT(skb, T_ctx_my_addr, tconn->my_addr_len, &tconn->my_addr); | ||
2569 | if (tconn->peer_addr_len) | ||
2570 | NLA_PUT(skb, T_ctx_peer_addr, tconn->peer_addr_len, &tconn->peer_addr); | ||
2541 | nla_nest_end(skb, nla); | 2571 | nla_nest_end(skb, nla); |
2542 | return 0; | 2572 | return 0; |
2543 | 2573 | ||
@@ -2574,7 +2604,7 @@ int nla_put_status_info(struct sk_buff *skb, struct drbd_conf *mdev, | |||
2574 | 2604 | ||
2575 | /* We need to add connection name and volume number information still. | 2605 | /* We need to add connection name and volume number information still. |
2576 | * Minor number is in drbd_genlmsghdr. */ | 2606 | * Minor number is in drbd_genlmsghdr. */ |
2577 | if (nla_put_drbd_cfg_context(skb, mdev->tconn->name, mdev->vnr)) | 2607 | if (nla_put_drbd_cfg_context(skb, mdev->tconn, mdev->vnr)) |
2578 | goto nla_put_failure; | 2608 | goto nla_put_failure; |
2579 | 2609 | ||
2580 | if (res_opts_to_skb(skb, &mdev->tconn->res_opts, exclude_sensitive)) | 2610 | if (res_opts_to_skb(skb, &mdev->tconn->res_opts, exclude_sensitive)) |
@@ -2736,7 +2766,7 @@ next_tconn: | |||
2736 | /* this is a tconn without a single volume */ | 2766 | /* this is a tconn without a single volume */ |
2737 | dh->minor = -1U; | 2767 | dh->minor = -1U; |
2738 | dh->ret_code = NO_ERROR; | 2768 | dh->ret_code = NO_ERROR; |
2739 | if (nla_put_drbd_cfg_context(skb, tconn->name, VOLUME_UNSPECIFIED)) | 2769 | if (nla_put_drbd_cfg_context(skb, tconn, VOLUME_UNSPECIFIED)) |
2740 | genlmsg_cancel(skb, dh); | 2770 | genlmsg_cancel(skb, dh); |
2741 | else | 2771 | else |
2742 | genlmsg_end(skb, dh); | 2772 | genlmsg_end(skb, dh); |