Pedro Tammela says: ==================== net/sched: fix action bind logic Some actions are not handling the case where an action can be created and bound to a filter independently. These actions are checking for parameters only passed in the netlink message for create/change/replace, which then errors out for valid uses like: tc filter ... action pedit index 1 In the iproute2 side, we saw a couple of actions with their parsers broken when passing "index 1" as the only action argument, while the kernel side accepted it correctly. We fixed those as well. ====================
Unified · Split
| @@ -190,40 +190,67 @@ static int tcf_mpls_init(struct net *net, struct nlattr *nla, · 189 unmodified lines | |||
| 190 | parm = nla_data(tb[TCA_MPLS_PARMS]); | 190 | parm = nla_data(tb[TCA_MPLS_PARMS]); |
| 191 | index = parm->index; | 191 | index = parm->index; |
| 192 | 192 | ||
| 193 | err = tcf_idr_check_alloc(tn, &index, a, bind); | ||
| 194 | if (err < 0) | ||
| 195 | return err; | ||
| 196 | exists = err; | ||
| 197 | if (exists && bind) | ||
| 198 | return 0; | ||
| 199 | |||
| 200 | if (!exists) { | ||
| 201 | ret = tcf_idr_create(tn, index, est, a, &act_mpls_ops, bind, | ||
| 202 | true, flags); | ||
| 203 | if (ret) { | ||
| 204 | tcf_idr_cleanup(tn, index); | ||
| 205 | return ret; | ||
| 206 | } | ||
| 207 | |||
| 208 | ret = ACT_P_CREATED; | ||
| 209 | } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { | ||
| 210 | tcf_idr_release(*a, bind); | ||
| 211 | return -EEXIST; | ||
| 212 | } | ||
| 213 | |||
| 193 | /* Verify parameters against action type. */ | 214 | /* Verify parameters against action type. */ |
| 194 | switch (parm->m_action) { | 215 | switch (parm->m_action) { |
| 195 | case TCA_MPLS_ACT_POP: | 216 | case TCA_MPLS_ACT_POP: |
| 196 | if (!tb[TCA_MPLS_PROTO]) { | 217 | if (!tb[TCA_MPLS_PROTO]) { |
| 197 | NL_SET_ERR_MSG_MOD(extack, "Protocol must be set for MPLS pop"); | 218 | NL_SET_ERR_MSG_MOD(extack, "Protocol must be set for MPLS pop"); |
| 198 | return -EINVAL; | 219 | err = -EINVAL; |
| 220 | goto release_idr; | ||
| 199 | } | 221 | } |
| 200 | if (!eth_proto_is_802_3(nla_get_be16(tb[TCA_MPLS_PROTO]))) { | 222 | if (!eth_proto_is_802_3(nla_get_be16(tb[TCA_MPLS_PROTO]))) { |
| 201 | NL_SET_ERR_MSG_MOD(extack, "Invalid protocol type for MPLS pop"); | 223 | NL_SET_ERR_MSG_MOD(extack, "Invalid protocol type for MPLS pop"); |
| 202 | return -EINVAL; | 224 | err = -EINVAL; |
| 225 | goto release_idr; | ||
| 203 | } | 226 | } |
| 204 | if (tb[TCA_MPLS_LABEL] || tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || | 227 | if (tb[TCA_MPLS_LABEL] || tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || |
| 205 | tb[TCA_MPLS_BOS]) { | 228 | tb[TCA_MPLS_BOS]) { |
| 206 | NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC or BOS cannot be used with MPLS pop"); | 229 | NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC or BOS cannot be used with MPLS pop"); |
| 207 | return -EINVAL; | 230 | err = -EINVAL; |
| 231 | goto release_idr; | ||
| 208 | } | 232 | } |
| 209 | break; | 233 | break; |
| 210 | case TCA_MPLS_ACT_DEC_TTL: | 234 | case TCA_MPLS_ACT_DEC_TTL: |
| 211 | if (tb[TCA_MPLS_PROTO] || tb[TCA_MPLS_LABEL] || | 235 | if (tb[TCA_MPLS_PROTO] || tb[TCA_MPLS_LABEL] || |
| 212 | tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || tb[TCA_MPLS_BOS]) { | 236 | tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || tb[TCA_MPLS_BOS]) { |
| 213 | NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC, BOS or protocol cannot be used with MPLS dec_ttl"); | 237 | NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC, BOS or protocol cannot be used with MPLS dec_ttl"); |
| 214 | return -EINVAL; | 238 | err = -EINVAL; |
| 239 | goto release_idr; | ||
| 215 | } | 240 | } |
| 216 | break; | 241 | break; |
| 217 | case TCA_MPLS_ACT_PUSH: | 242 | case TCA_MPLS_ACT_PUSH: |
| 218 | case TCA_MPLS_ACT_MAC_PUSH: | 243 | case TCA_MPLS_ACT_MAC_PUSH: |
| 219 | if (!tb[TCA_MPLS_LABEL]) { | 244 | if (!tb[TCA_MPLS_LABEL]) { |
| 220 | NL_SET_ERR_MSG_MOD(extack, "Label is required for MPLS push"); | 245 | NL_SET_ERR_MSG_MOD(extack, "Label is required for MPLS push"); |
| 221 | return -EINVAL; | 246 | err = -EINVAL; |
| 247 | goto release_idr; | ||
| 222 | } | 248 | } |
| 223 | if (tb[TCA_MPLS_PROTO] && | 249 | if (tb[TCA_MPLS_PROTO] && |
| 224 | !eth_p_mpls(nla_get_be16(tb[TCA_MPLS_PROTO]))) { | 250 | !eth_p_mpls(nla_get_be16(tb[TCA_MPLS_PROTO]))) { |
| 225 | NL_SET_ERR_MSG_MOD(extack, "Protocol must be an MPLS type for MPLS push"); | 251 | NL_SET_ERR_MSG_MOD(extack, "Protocol must be an MPLS type for MPLS push"); |
| 226 | return -EPROTONOSUPPORT; | 252 | err = -EPROTONOSUPPORT; |
| 253 | goto release_idr; | ||
| 227 | } | 254 | } |
| 228 | /* Push needs a TTL - if not specified, set a default value. */ | 255 | /* Push needs a TTL - if not specified, set a default value. */ |
| 229 | if (!tb[TCA_MPLS_TTL]) { | 256 | if (!tb[TCA_MPLS_TTL]) { |
| @@ -238,33 +265,14 @@ static int tcf_mpls_init(struct net *net, struct nlattr *nla, · 8 unmodified lines | |||
| 238 | case TCA_MPLS_ACT_MODIFY: | 265 | case TCA_MPLS_ACT_MODIFY: |
| 239 | if (tb[TCA_MPLS_PROTO]) { | 266 | if (tb[TCA_MPLS_PROTO]) { |
| 240 | NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be used with MPLS modify"); | 267 | NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be used with MPLS modify"); |
| 241 | return -EINVAL; | 268 | err = -EINVAL; |
| 269 | goto release_idr; | ||
| 242 | } | 270 | } |
| 243 | break; | 271 | break; |
| 244 | default: | 272 | default: |
| 245 | NL_SET_ERR_MSG_MOD(extack, "Unknown MPLS action"); | 273 | NL_SET_ERR_MSG_MOD(extack, "Unknown MPLS action"); |
| 246 | return -EINVAL; | 274 | err = -EINVAL; |
| 247 | } | 275 | goto release_idr; |
| 248 | |||
| 249 | err = tcf_idr_check_alloc(tn, &index, a, bind); | ||
| 250 | if (err < 0) | ||
| 251 | return err; | ||
| 252 | exists = err; | ||
| 253 | if (exists && bind) | ||
| 254 | return 0; | ||
| 255 | |||
| 256 | if (!exists) { | ||
| 257 | ret = tcf_idr_create(tn, index, est, a, | ||
| 258 | &act_mpls_ops, bind, true, flags); | ||
| 259 | if (ret) { | ||
| 260 | tcf_idr_cleanup(tn, index); | ||
| 261 | return ret; | ||
| 262 | } | ||
| 263 | |||
| 264 | ret = ACT_P_CREATED; | ||
| 265 | } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { | ||
| 266 | tcf_idr_release(*a, bind); | ||
| 267 | return -EEXIST; | ||
| 268 | } | 276 | } |
| 269 | 277 | ||
| 270 | err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); | 278 | err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); |
| @@ -181,26 +181,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 180 unmodified lines | |||
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | parm = nla_data(pattr); | 183 | parm = nla_data(pattr); |
| 184 | if (!parm->nkeys) { | ||
| 185 | NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed"); | ||
| 186 | return -EINVAL; | ||
| 187 | } | ||
| 188 | ksize = parm->nkeys * sizeof(struct tc_pedit_key); | ||
| 189 | if (nla_len(pattr) < sizeof(*parm) + ksize) { | ||
| 190 | NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid"); | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | |||
| 194 | nparms = kzalloc(sizeof(*nparms), GFP_KERNEL); | ||
| 195 | if (!nparms) | ||
| 196 | return -ENOMEM; | ||
| 197 | |||
| 198 | nparms->tcfp_keys_ex = | ||
| 199 | tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys); | ||
| 200 | if (IS_ERR(nparms->tcfp_keys_ex)) { | ||
| 201 | ret = PTR_ERR(nparms->tcfp_keys_ex); | ||
| 202 | goto out_free; | ||
| 203 | } | ||
| 204 | 184 | ||
| 205 | index = parm->index; | 185 | index = parm->index; |
| 206 | err = tcf_idr_check_alloc(tn, &index, a, bind); | 186 | err = tcf_idr_check_alloc(tn, &index, a, bind); |
| @@ -209,25 +189,49 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 2 unmodified lines | |||
| 209 | &act_pedit_ops, bind, flags); | 189 | &act_pedit_ops, bind, flags); |
| 210 | if (ret) { | 190 | if (ret) { |
| 211 | tcf_idr_cleanup(tn, index); | 191 | tcf_idr_cleanup(tn, index); |
| 212 | goto out_free_ex; | 192 | return ret; |
| 213 | } | 193 | } |
| 214 | ret = ACT_P_CREATED; | 194 | ret = ACT_P_CREATED; |
| 215 | } else if (err > 0) { | 195 | } else if (err > 0) { |
| 216 | if (bind) | 196 | if (bind) |
| 217 | goto out_free; | 197 | return 0; |
| 218 | if (!(flags & TCA_ACT_FLAGS_REPLACE)) { | 198 | if (!(flags & TCA_ACT_FLAGS_REPLACE)) { |
| 219 | ret = -EEXIST; | 199 | ret = -EEXIST; |
| 220 | goto out_release; | 200 | goto out_release; |
| 221 | } | 201 | } |
| 222 | } else { | 202 | } else { |
| 223 | ret = err; | 203 | return err; |
| 224 | goto out_free_ex; | 204 | } |
| 205 | |||
| 206 | if (!parm->nkeys) { | ||
| 207 | NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed"); | ||
| 208 | ret = -EINVAL; | ||
| 209 | goto out_release; | ||
| 210 | } | ||
| 211 | ksize = parm->nkeys * sizeof(struct tc_pedit_key); | ||
| 212 | if (nla_len(pattr) < sizeof(*parm) + ksize) { | ||
| 213 | NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid"); | ||
| 214 | ret = -EINVAL; | ||
| 215 | goto out_release; | ||
| 216 | } | ||
| 217 | |||
| 218 | nparms = kzalloc(sizeof(*nparms), GFP_KERNEL); | ||
| 219 | if (!nparms) { | ||
| 220 | ret = -ENOMEM; | ||
| 221 | goto out_release; | ||
| 222 | } | ||
| 223 | |||
| 224 | nparms->tcfp_keys_ex = | ||
| 225 | tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys); | ||
| 226 | if (IS_ERR(nparms->tcfp_keys_ex)) { | ||
| 227 | ret = PTR_ERR(nparms->tcfp_keys_ex); | ||
| 228 | goto out_free; | ||
| 225 | } | 229 | } |
| 226 | 230 | ||
| 227 | err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); | 231 | err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); |
| 228 | if (err < 0) { | 232 | if (err < 0) { |
| 229 | ret = err; | 233 | ret = err; |
| 230 | goto out_release; | 234 | goto out_free_ex; |
| 231 | } | 235 | } |
| 232 | 236 | ||
| 233 | nparms->tcfp_off_max_hint = 0; | 237 | nparms->tcfp_off_max_hint = 0; |
| @@ -278,12 +282,12 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 44 unmodified lines | |||
| 278 | put_chain: | 282 | put_chain: |
| 279 | if (goto_ch) | 283 | if (goto_ch) |
| 280 | tcf_chain_put_by_act(goto_ch); | 284 | tcf_chain_put_by_act(goto_ch); |
| 281 | out_release: | ||
| 282 | tcf_idr_release(*a, bind); | ||
| 283 | out_free_ex: | 285 | out_free_ex: |
| 284 | kfree(nparms->tcfp_keys_ex); | 286 | kfree(nparms->tcfp_keys_ex); |
| 285 | out_free: | 287 | out_free: |
| 286 | kfree(nparms); | 288 | kfree(nparms); |
| 289 | out_release: | ||
| 290 | tcf_idr_release(*a, bind); | ||
| 287 | return ret; | 291 | return ret; |
| 288 | } | 292 | } |
| 289 | 293 | ||
| @@ -55,8 +55,8 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, · 54 unmodified lines | |||
| 55 | sample_policy, NULL); | 55 | sample_policy, NULL); |
| 56 | if (ret < 0) | 56 | if (ret < 0) |
| 57 | return ret; | 57 | return ret; |
| 58 | if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] || | 58 | |
| 59 | !tb[TCA_SAMPLE_PSAMPLE_GROUP]) | 59 | if (!tb[TCA_SAMPLE_PARMS]) |
| 60 | return -EINVAL; | 60 | return -EINVAL; |
| 61 | 61 | ||
| 62 | parm = nla_data(tb[TCA_SAMPLE_PARMS]); | 62 | parm = nla_data(tb[TCA_SAMPLE_PARMS]); |
| @@ -80,6 +80,13 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, · 17 unmodified lines | |||
| 80 | tcf_idr_release(*a, bind); | 80 | tcf_idr_release(*a, bind); |
| 81 | return -EEXIST; | 81 | return -EEXIST; |
| 82 | } | 82 | } |
| 83 | |||
| 84 | if (!tb[TCA_SAMPLE_RATE] || !tb[TCA_SAMPLE_PSAMPLE_GROUP]) { | ||
| 85 | NL_SET_ERR_MSG(extack, "sample rate and group are required"); | ||
| 86 | err = -EINVAL; | ||
| 87 | goto release_idr; | ||
| 88 | } | ||
| 89 | |||
| 83 | err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); | 90 | err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); |
| 84 | if (err < 0) | 91 | if (err < 0) |
| 85 | goto release_idr; | 92 | goto release_idr; |