| 858 | } |
| 859 | |
| 860 | int |
| 861 | rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, |
| 862 | struct rib_cmd_info *rc) |
| 863 | { |
| 864 | RIB_RLOCK_TRACKER; |
| 865 | struct route_nhop_data rnd_orig; |
| 866 | struct rib_head *rnh; |
| 867 | struct rtentry *rt; |
| 868 | int error; |
| 869 | |
| 870 | NET_EPOCH_ASSERT(); |
| 871 | |
| 872 | rnh = get_rnh(fibnum, info); |
| 873 | if (rnh == NULL) |
| 874 | return (EAFNOSUPPORT); |
| 875 | |
| 876 | bzero(rc, sizeof(struct rib_cmd_info)); |
| 877 | rc->rc_cmd = RTM_CHANGE; |
| 878 | |
| 879 | /* Check if updated gateway exists */ |
| 880 | if ((info->rti_flags & RTF_GATEWAY) && |
| 881 | (info->rti_info[RTAX_GATEWAY] == NULL)) { |
| 882 | |
| 883 | /* |
| 884 | * route(8) adds RTF_GATEWAY flag if -interface is not set. |
| 885 | * Remove RTF_GATEWAY to enforce consistency and maintain |
| 886 | * compatibility.. |
| 887 | */ |
| 888 | info->rti_flags &= ~RTF_GATEWAY; |
| 889 | } |
| 890 | |
| 891 | /* |
| 892 | * route change is done in multiple steps, with dropping and |
| 893 | * reacquiring lock. In the situations with multiple processes |
| 894 | * changes the same route in can lead to the case when route |
| 895 | * is changed between the steps. Address it by retrying the operation |
| 896 | * multiple times before failing. |
| 897 | */ |
| 898 | |
| 899 | RIB_RLOCK(rnh); |
| 900 | rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], |
| 901 | info->rti_info[RTAX_NETMASK], &rnh->head); |
| 902 | |
| 903 | if (rt == NULL) { |
| 904 | RIB_RUNLOCK(rnh); |
| 905 | return (ESRCH); |
| 906 | } |
| 907 | |
| 908 | rnd_orig.rnd_nhop = rt->rt_nhop; |
| 909 | rnd_orig.rnd_weight = rt->rt_weight; |
| 910 | |
| 911 | RIB_RUNLOCK(rnh); |
| 912 | |
| 913 | for (int i = 0; i < RIB_MAX_RETRIES; i++) { |
| 914 | error = change_route(rnh, info, &rnd_orig, rc); |
| 915 | if (error != EAGAIN) |
| 916 | break; |
| 917 | } |
no test coverage detected