diff options
-rw-r--r-- | include/net/if_inet6.h | 9 | ||||
-rw-r--r-- | include/net/mld.h | 27 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 116 |
3 files changed, 143 insertions, 9 deletions
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 736b5fb95474..02ef7727bb55 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h | |||
@@ -171,12 +171,17 @@ struct inet6_dev { | |||
171 | struct ifmcaddr6 *mc_list; | 171 | struct ifmcaddr6 *mc_list; |
172 | struct ifmcaddr6 *mc_tomb; | 172 | struct ifmcaddr6 *mc_tomb; |
173 | spinlock_t mc_lock; | 173 | spinlock_t mc_lock; |
174 | unsigned char mc_qrv; | 174 | |
175 | unsigned char mc_qrv; /* Query Robustness Variable */ | ||
175 | unsigned char mc_gq_running; | 176 | unsigned char mc_gq_running; |
176 | unsigned char mc_ifc_count; | 177 | unsigned char mc_ifc_count; |
177 | unsigned char mc_dad_count; | 178 | unsigned char mc_dad_count; |
178 | unsigned long mc_v1_seen; | 179 | |
180 | unsigned long mc_v1_seen; /* Max time we stay in MLDv1 mode */ | ||
181 | unsigned long mc_qi; /* Query Interval */ | ||
182 | unsigned long mc_qri; /* Query Response Interval */ | ||
179 | unsigned long mc_maxdelay; | 183 | unsigned long mc_maxdelay; |
184 | |||
180 | struct timer_list mc_gq_timer; /* general query timer */ | 185 | struct timer_list mc_gq_timer; /* general query timer */ |
181 | struct timer_list mc_ifc_timer; /* interface change timer */ | 186 | struct timer_list mc_ifc_timer; /* interface change timer */ |
182 | struct timer_list mc_dad_timer; /* dad complete mc timer */ | 187 | struct timer_list mc_dad_timer; /* dad complete mc timer */ |
diff --git a/include/net/mld.h b/include/net/mld.h index 467143cd4e2f..2b5421f6a7c2 100644 --- a/include/net/mld.h +++ b/include/net/mld.h | |||
@@ -63,7 +63,7 @@ struct mld2_query { | |||
63 | #define mld2q_mrc mld2q_hdr.icmp6_maxdelay | 63 | #define mld2q_mrc mld2q_hdr.icmp6_maxdelay |
64 | #define mld2q_resv1 mld2q_hdr.icmp6_dataun.un_data16[1] | 64 | #define mld2q_resv1 mld2q_hdr.icmp6_dataun.un_data16[1] |
65 | 65 | ||
66 | /* Max Response Code */ | 66 | /* Max Response Code, TODO: transform this to use the below */ |
67 | #define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value)) | 67 | #define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value)) |
68 | #define MLDV2_EXP(thresh, nbmant, nbexp, value) \ | 68 | #define MLDV2_EXP(thresh, nbmant, nbexp, value) \ |
69 | ((value) < (thresh) ? (value) : \ | 69 | ((value) < (thresh) ? (value) : \ |
@@ -72,4 +72,29 @@ struct mld2_query { | |||
72 | 72 | ||
73 | #define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value) | 73 | #define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value) |
74 | 74 | ||
75 | /* RFC3810, 5.1.3. Maximum Response Code: | ||
76 | * | ||
77 | * If Maximum Response Code >= 32768, Maximum Response Code represents a | ||
78 | * floating-point value as follows: | ||
79 | * | ||
80 | * 0 1 2 3 4 5 6 7 8 9 A B C D E F | ||
81 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
82 | * |1| exp | mant | | ||
83 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
84 | */ | ||
85 | #define MLDV2_MRC_EXP(value) (((value) >> 12) & 0x0007) | ||
86 | #define MLDV2_MRC_MAN(value) ((value) & 0x0fff) | ||
87 | |||
88 | /* RFC3810, 5.1.9. QQIC (Querier's Query Interval Code): | ||
89 | * | ||
90 | * If QQIC >= 128, QQIC represents a floating-point value as follows: | ||
91 | * | ||
92 | * 0 1 2 3 4 5 6 7 | ||
93 | * +-+-+-+-+-+-+-+-+ | ||
94 | * |1| exp | mant | | ||
95 | * +-+-+-+-+-+-+-+-+ | ||
96 | */ | ||
97 | #define MLDV2_QQIC_EXP(value) (((value) >> 4) & 0x07) | ||
98 | #define MLDV2_QQIC_MAN(value) ((value) & 0x0f) | ||
99 | |||
75 | #endif | 100 | #endif |
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 98ead2b1a669..8992ff262789 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -108,6 +108,10 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, | |||
108 | struct inet6_dev *idev); | 108 | struct inet6_dev *idev); |
109 | 109 | ||
110 | #define MLD_QRV_DEFAULT 2 | 110 | #define MLD_QRV_DEFAULT 2 |
111 | /* RFC3810, 9.2. Query Interval */ | ||
112 | #define MLD_QI_DEFAULT (125 * HZ) | ||
113 | /* RFC3810, 9.3. Query Response Interval */ | ||
114 | #define MLD_QRI_DEFAULT (10 * HZ) | ||
111 | 115 | ||
112 | /* RFC3810, 8.1 Query Version Distinctions */ | 116 | /* RFC3810, 8.1 Query Version Distinctions */ |
113 | #define MLD_V1_QUERY_LEN 24 | 117 | #define MLD_V1_QUERY_LEN 24 |
@@ -1112,6 +1116,93 @@ static bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, | |||
1112 | return true; | 1116 | return true; |
1113 | } | 1117 | } |
1114 | 1118 | ||
1119 | static void mld_set_v1_mode(struct inet6_dev *idev) | ||
1120 | { | ||
1121 | /* RFC3810, relevant sections: | ||
1122 | * - 9.1. Robustness Variable | ||
1123 | * - 9.2. Query Interval | ||
1124 | * - 9.3. Query Response Interval | ||
1125 | * - 9.12. Older Version Querier Present Timeout | ||
1126 | */ | ||
1127 | unsigned long switchback; | ||
1128 | |||
1129 | switchback = (idev->mc_qrv * idev->mc_qi) + idev->mc_qri; | ||
1130 | |||
1131 | idev->mc_v1_seen = jiffies + switchback; | ||
1132 | } | ||
1133 | |||
1134 | static void mld_update_qrv(struct inet6_dev *idev, | ||
1135 | const struct mld2_query *mlh2) | ||
1136 | { | ||
1137 | /* RFC3810, relevant sections: | ||
1138 | * - 5.1.8. QRV (Querier's Robustness Variable) | ||
1139 | * - 9.1. Robustness Variable | ||
1140 | */ | ||
1141 | |||
1142 | /* The value of the Robustness Variable MUST NOT be zero, | ||
1143 | * and SHOULD NOT be one. Catch this here if we ever run | ||
1144 | * into such a case in future. | ||
1145 | */ | ||
1146 | WARN_ON(idev->mc_qrv == 0); | ||
1147 | |||
1148 | if (mlh2->mld2q_qrv > 0) | ||
1149 | idev->mc_qrv = mlh2->mld2q_qrv; | ||
1150 | |||
1151 | if (unlikely(idev->mc_qrv < 2)) { | ||
1152 | net_warn_ratelimited("IPv6: MLD: clamping QRV from %u to %u!\n", | ||
1153 | idev->mc_qrv, MLD_QRV_DEFAULT); | ||
1154 | idev->mc_qrv = MLD_QRV_DEFAULT; | ||
1155 | } | ||
1156 | } | ||
1157 | |||
1158 | static void mld_update_qi(struct inet6_dev *idev, | ||
1159 | const struct mld2_query *mlh2) | ||
1160 | { | ||
1161 | /* RFC3810, relevant sections: | ||
1162 | * - 5.1.9. QQIC (Querier's Query Interval Code) | ||
1163 | * - 9.2. Query Interval | ||
1164 | * - 9.12. Older Version Querier Present Timeout | ||
1165 | * (the [Query Interval] in the last Query received) | ||
1166 | */ | ||
1167 | unsigned long mc_qqi; | ||
1168 | |||
1169 | if (mlh2->mld2q_qqic < 128) { | ||
1170 | mc_qqi = mlh2->mld2q_qqic; | ||
1171 | } else { | ||
1172 | unsigned long mc_man, mc_exp; | ||
1173 | |||
1174 | mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic); | ||
1175 | mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic); | ||
1176 | |||
1177 | mc_qqi = (mc_man | 0x10) << (mc_exp + 3); | ||
1178 | } | ||
1179 | |||
1180 | idev->mc_qi = mc_qqi * HZ; | ||
1181 | } | ||
1182 | |||
1183 | static void mld_update_qri(struct inet6_dev *idev, | ||
1184 | const struct mld2_query *mlh2) | ||
1185 | { | ||
1186 | /* RFC3810, relevant sections: | ||
1187 | * - 5.1.3. Maximum Response Code | ||
1188 | * - 9.3. Query Response Interval | ||
1189 | */ | ||
1190 | unsigned long mc_qri, mc_mrc = ntohs(mlh2->mld2q_mrc); | ||
1191 | |||
1192 | if (mc_mrc < 32768) { | ||
1193 | mc_qri = mc_mrc; | ||
1194 | } else { | ||
1195 | unsigned long mc_man, mc_exp; | ||
1196 | |||
1197 | mc_exp = MLDV2_MRC_EXP(mc_mrc); | ||
1198 | mc_man = MLDV2_MRC_MAN(mc_mrc); | ||
1199 | |||
1200 | mc_qri = (mc_man | 0x1000) << (mc_exp + 3); | ||
1201 | } | ||
1202 | |||
1203 | idev->mc_qri = msecs_to_jiffies(mc_qri); | ||
1204 | } | ||
1205 | |||
1115 | /* called with rcu_read_lock() */ | 1206 | /* called with rcu_read_lock() */ |
1116 | int igmp6_event_query(struct sk_buff *skb) | 1207 | int igmp6_event_query(struct sk_buff *skb) |
1117 | { | 1208 | { |
@@ -1150,12 +1241,15 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1150 | return -EINVAL; | 1241 | return -EINVAL; |
1151 | 1242 | ||
1152 | if (len == MLD_V1_QUERY_LEN) { | 1243 | if (len == MLD_V1_QUERY_LEN) { |
1153 | int switchback; | ||
1154 | /* MLDv1 router present */ | 1244 | /* MLDv1 router present */ |
1155 | |||
1156 | max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay)); | 1245 | max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay)); |
1157 | switchback = (idev->mc_qrv + 1) * max_delay; | 1246 | |
1158 | idev->mc_v1_seen = jiffies + switchback; | 1247 | mld_set_v1_mode(idev); |
1248 | |||
1249 | /* cancel MLDv2 report timer */ | ||
1250 | idev->mc_gq_running = 0; | ||
1251 | if (del_timer(&idev->mc_gq_timer)) | ||
1252 | __in6_dev_put(idev); | ||
1159 | 1253 | ||
1160 | /* cancel the interface change timer */ | 1254 | /* cancel the interface change timer */ |
1161 | idev->mc_ifc_count = 0; | 1255 | idev->mc_ifc_count = 0; |
@@ -1166,6 +1260,10 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1166 | } else if (len >= MLD_V2_QUERY_LEN_MIN) { | 1260 | } else if (len >= MLD_V2_QUERY_LEN_MIN) { |
1167 | int srcs_offset = sizeof(struct mld2_query) - | 1261 | int srcs_offset = sizeof(struct mld2_query) - |
1168 | sizeof(struct icmp6hdr); | 1262 | sizeof(struct icmp6hdr); |
1263 | |||
1264 | /* hosts need to stay in MLDv1 mode, discard MLDv2 queries */ | ||
1265 | if (MLD_V1_SEEN(idev)) | ||
1266 | return 0; | ||
1169 | if (!pskb_may_pull(skb, srcs_offset)) | 1267 | if (!pskb_may_pull(skb, srcs_offset)) |
1170 | return -EINVAL; | 1268 | return -EINVAL; |
1171 | 1269 | ||
@@ -1175,8 +1273,10 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1175 | 1273 | ||
1176 | idev->mc_maxdelay = max_delay; | 1274 | idev->mc_maxdelay = max_delay; |
1177 | 1275 | ||
1178 | if (mlh2->mld2q_qrv) | 1276 | mld_update_qrv(idev, mlh2); |
1179 | idev->mc_qrv = mlh2->mld2q_qrv; | 1277 | mld_update_qi(idev, mlh2); |
1278 | mld_update_qri(idev, mlh2); | ||
1279 | |||
1180 | if (group_type == IPV6_ADDR_ANY) { /* general query */ | 1280 | if (group_type == IPV6_ADDR_ANY) { /* general query */ |
1181 | if (mlh2->mld2q_nsrcs) | 1281 | if (mlh2->mld2q_nsrcs) |
1182 | return -EINVAL; /* no sources allowed */ | 1282 | return -EINVAL; /* no sources allowed */ |
@@ -2337,7 +2437,11 @@ void ipv6_mc_init_dev(struct inet6_dev *idev) | |||
2337 | (unsigned long)idev); | 2437 | (unsigned long)idev); |
2338 | setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire, | 2438 | setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire, |
2339 | (unsigned long)idev); | 2439 | (unsigned long)idev); |
2440 | |||
2340 | idev->mc_qrv = MLD_QRV_DEFAULT; | 2441 | idev->mc_qrv = MLD_QRV_DEFAULT; |
2442 | idev->mc_qi = MLD_QI_DEFAULT; | ||
2443 | idev->mc_qri = MLD_QRI_DEFAULT; | ||
2444 | |||
2341 | idev->mc_maxdelay = unsolicited_report_interval(idev); | 2445 | idev->mc_maxdelay = unsolicited_report_interval(idev); |
2342 | idev->mc_v1_seen = 0; | 2446 | idev->mc_v1_seen = 0; |
2343 | write_unlock_bh(&idev->lock); | 2447 | write_unlock_bh(&idev->lock); |