aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/if_inet6.h9
-rw-r--r--include/net/mld.h27
-rw-r--r--net/ipv6/mcast.c116
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
1119static 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
1134static 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
1158static 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
1183static 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() */
1116int igmp6_event_query(struct sk_buff *skb) 1207int 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);