.. | .. |
---|
228 | 228 | if (c->mfc_flags & MFC_OFFLOAD) |
---|
229 | 229 | rtm->rtm_flags |= RTNH_F_OFFLOAD; |
---|
230 | 230 | |
---|
231 | | - mp_attr = nla_nest_start(skb, RTA_MULTIPATH); |
---|
| 231 | + mp_attr = nla_nest_start_noflag(skb, RTA_MULTIPATH); |
---|
232 | 232 | if (!mp_attr) |
---|
233 | 233 | return -EMSGSIZE; |
---|
234 | 234 | |
---|
.. | .. |
---|
268 | 268 | } |
---|
269 | 269 | EXPORT_SYMBOL(mr_fill_mroute); |
---|
270 | 270 | |
---|
| 271 | +static bool mr_mfc_uses_dev(const struct mr_table *mrt, |
---|
| 272 | + const struct mr_mfc *c, |
---|
| 273 | + const struct net_device *dev) |
---|
| 274 | +{ |
---|
| 275 | + int ct; |
---|
| 276 | + |
---|
| 277 | + for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { |
---|
| 278 | + if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { |
---|
| 279 | + const struct vif_device *vif; |
---|
| 280 | + |
---|
| 281 | + vif = &mrt->vif_table[ct]; |
---|
| 282 | + if (vif->dev == dev) |
---|
| 283 | + return true; |
---|
| 284 | + } |
---|
| 285 | + } |
---|
| 286 | + return false; |
---|
| 287 | +} |
---|
| 288 | + |
---|
| 289 | +int mr_table_dump(struct mr_table *mrt, struct sk_buff *skb, |
---|
| 290 | + struct netlink_callback *cb, |
---|
| 291 | + int (*fill)(struct mr_table *mrt, struct sk_buff *skb, |
---|
| 292 | + u32 portid, u32 seq, struct mr_mfc *c, |
---|
| 293 | + int cmd, int flags), |
---|
| 294 | + spinlock_t *lock, struct fib_dump_filter *filter) |
---|
| 295 | +{ |
---|
| 296 | + unsigned int e = 0, s_e = cb->args[1]; |
---|
| 297 | + unsigned int flags = NLM_F_MULTI; |
---|
| 298 | + struct mr_mfc *mfc; |
---|
| 299 | + int err; |
---|
| 300 | + |
---|
| 301 | + if (filter->filter_set) |
---|
| 302 | + flags |= NLM_F_DUMP_FILTERED; |
---|
| 303 | + |
---|
| 304 | + list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) { |
---|
| 305 | + if (e < s_e) |
---|
| 306 | + goto next_entry; |
---|
| 307 | + if (filter->dev && |
---|
| 308 | + !mr_mfc_uses_dev(mrt, mfc, filter->dev)) |
---|
| 309 | + goto next_entry; |
---|
| 310 | + |
---|
| 311 | + err = fill(mrt, skb, NETLINK_CB(cb->skb).portid, |
---|
| 312 | + cb->nlh->nlmsg_seq, mfc, RTM_NEWROUTE, flags); |
---|
| 313 | + if (err < 0) |
---|
| 314 | + goto out; |
---|
| 315 | +next_entry: |
---|
| 316 | + e++; |
---|
| 317 | + } |
---|
| 318 | + |
---|
| 319 | + spin_lock_bh(lock); |
---|
| 320 | + list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) { |
---|
| 321 | + if (e < s_e) |
---|
| 322 | + goto next_entry2; |
---|
| 323 | + if (filter->dev && |
---|
| 324 | + !mr_mfc_uses_dev(mrt, mfc, filter->dev)) |
---|
| 325 | + goto next_entry2; |
---|
| 326 | + |
---|
| 327 | + err = fill(mrt, skb, NETLINK_CB(cb->skb).portid, |
---|
| 328 | + cb->nlh->nlmsg_seq, mfc, RTM_NEWROUTE, flags); |
---|
| 329 | + if (err < 0) { |
---|
| 330 | + spin_unlock_bh(lock); |
---|
| 331 | + goto out; |
---|
| 332 | + } |
---|
| 333 | +next_entry2: |
---|
| 334 | + e++; |
---|
| 335 | + } |
---|
| 336 | + spin_unlock_bh(lock); |
---|
| 337 | + err = 0; |
---|
| 338 | +out: |
---|
| 339 | + cb->args[1] = e; |
---|
| 340 | + return err; |
---|
| 341 | +} |
---|
| 342 | +EXPORT_SYMBOL(mr_table_dump); |
---|
| 343 | + |
---|
271 | 344 | int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, |
---|
272 | 345 | struct mr_table *(*iter)(struct net *net, |
---|
273 | 346 | struct mr_table *mrt), |
---|
.. | .. |
---|
275 | 348 | struct sk_buff *skb, |
---|
276 | 349 | u32 portid, u32 seq, struct mr_mfc *c, |
---|
277 | 350 | int cmd, int flags), |
---|
278 | | - spinlock_t *lock) |
---|
| 351 | + spinlock_t *lock, struct fib_dump_filter *filter) |
---|
279 | 352 | { |
---|
280 | | - unsigned int t = 0, e = 0, s_t = cb->args[0], s_e = cb->args[1]; |
---|
| 353 | + unsigned int t = 0, s_t = cb->args[0]; |
---|
281 | 354 | struct net *net = sock_net(skb->sk); |
---|
282 | 355 | struct mr_table *mrt; |
---|
283 | | - struct mr_mfc *mfc; |
---|
| 356 | + int err; |
---|
| 357 | + |
---|
| 358 | + /* multicast does not track protocol or have route type other |
---|
| 359 | + * than RTN_MULTICAST |
---|
| 360 | + */ |
---|
| 361 | + if (filter->filter_set) { |
---|
| 362 | + if (filter->protocol || filter->flags || |
---|
| 363 | + (filter->rt_type && filter->rt_type != RTN_MULTICAST)) |
---|
| 364 | + return skb->len; |
---|
| 365 | + } |
---|
284 | 366 | |
---|
285 | 367 | rcu_read_lock(); |
---|
286 | 368 | for (mrt = iter(net, NULL); mrt; mrt = iter(net, mrt)) { |
---|
287 | 369 | if (t < s_t) |
---|
288 | 370 | goto next_table; |
---|
289 | | - list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) { |
---|
290 | | - if (e < s_e) |
---|
291 | | - goto next_entry; |
---|
292 | | - if (fill(mrt, skb, NETLINK_CB(cb->skb).portid, |
---|
293 | | - cb->nlh->nlmsg_seq, mfc, |
---|
294 | | - RTM_NEWROUTE, NLM_F_MULTI) < 0) |
---|
295 | | - goto done; |
---|
296 | | -next_entry: |
---|
297 | | - e++; |
---|
298 | | - } |
---|
299 | 371 | |
---|
300 | | - spin_lock_bh(lock); |
---|
301 | | - list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) { |
---|
302 | | - if (e < s_e) |
---|
303 | | - goto next_entry2; |
---|
304 | | - if (fill(mrt, skb, NETLINK_CB(cb->skb).portid, |
---|
305 | | - cb->nlh->nlmsg_seq, mfc, |
---|
306 | | - RTM_NEWROUTE, NLM_F_MULTI) < 0) { |
---|
307 | | - spin_unlock_bh(lock); |
---|
308 | | - goto done; |
---|
309 | | - } |
---|
310 | | -next_entry2: |
---|
311 | | - e++; |
---|
312 | | - } |
---|
313 | | - spin_unlock_bh(lock); |
---|
314 | | - e = 0; |
---|
315 | | - s_e = 0; |
---|
| 372 | + err = mr_table_dump(mrt, skb, cb, fill, lock, filter); |
---|
| 373 | + if (err < 0) |
---|
| 374 | + break; |
---|
| 375 | + cb->args[1] = 0; |
---|
316 | 376 | next_table: |
---|
317 | 377 | t++; |
---|
318 | 378 | } |
---|
319 | | -done: |
---|
320 | 379 | rcu_read_unlock(); |
---|
321 | 380 | |
---|
322 | | - cb->args[1] = e; |
---|
323 | 381 | cb->args[0] = t; |
---|
324 | 382 | |
---|
325 | 383 | return skb->len; |
---|
.. | .. |
---|
328 | 386 | |
---|
329 | 387 | int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family, |
---|
330 | 388 | int (*rules_dump)(struct net *net, |
---|
331 | | - struct notifier_block *nb), |
---|
| 389 | + struct notifier_block *nb, |
---|
| 390 | + struct netlink_ext_ack *extack), |
---|
332 | 391 | struct mr_table *(*mr_iter)(struct net *net, |
---|
333 | 392 | struct mr_table *mrt), |
---|
334 | | - rwlock_t *mrt_lock) |
---|
| 393 | + rwlock_t *mrt_lock, |
---|
| 394 | + struct netlink_ext_ack *extack) |
---|
335 | 395 | { |
---|
336 | 396 | struct mr_table *mrt; |
---|
337 | 397 | int err; |
---|
338 | 398 | |
---|
339 | | - err = rules_dump(net, nb); |
---|
| 399 | + err = rules_dump(net, nb, extack); |
---|
340 | 400 | if (err) |
---|
341 | 401 | return err; |
---|
342 | 402 | |
---|
.. | .. |
---|
351 | 411 | if (!v->dev) |
---|
352 | 412 | continue; |
---|
353 | 413 | |
---|
354 | | - mr_call_vif_notifier(nb, net, family, |
---|
355 | | - FIB_EVENT_VIF_ADD, |
---|
356 | | - v, vifi, mrt->id); |
---|
| 414 | + err = mr_call_vif_notifier(nb, family, |
---|
| 415 | + FIB_EVENT_VIF_ADD, |
---|
| 416 | + v, vifi, mrt->id, extack); |
---|
| 417 | + if (err) |
---|
| 418 | + break; |
---|
357 | 419 | } |
---|
358 | 420 | read_unlock(mrt_lock); |
---|
359 | 421 | |
---|
| 422 | + if (err) |
---|
| 423 | + return err; |
---|
| 424 | + |
---|
360 | 425 | /* Notify on table MFC entries */ |
---|
361 | | - list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) |
---|
362 | | - mr_call_mfc_notifier(nb, net, family, |
---|
363 | | - FIB_EVENT_ENTRY_ADD, |
---|
364 | | - mfc, mrt->id); |
---|
| 426 | + list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) { |
---|
| 427 | + err = mr_call_mfc_notifier(nb, family, |
---|
| 428 | + FIB_EVENT_ENTRY_ADD, |
---|
| 429 | + mfc, mrt->id, extack); |
---|
| 430 | + if (err) |
---|
| 431 | + return err; |
---|
| 432 | + } |
---|
365 | 433 | } |
---|
366 | 434 | |
---|
367 | 435 | return 0; |
---|