aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee80211
diff options
context:
space:
mode:
authorJames Ketrenos <jketreno@linux.intel.com>2005-09-21 12:54:53 -0400
committerJeff Garzik <jgarzik@pobox.com>2005-09-21 23:02:31 -0400
commit1264fc0498e1e20f97b1ab690e523e7a7fc50eab (patch)
tree000b8528685f3ca250d0581df88abef03417941d /net/ieee80211
parent3f552bbf8614d2d26f488ca0d3e188bdec484bf4 (diff)
[PATCH] ieee80211: Fix TKIP, repeated fragmentation problem, and payload_size reporting
tree 8428e9f510e6ad6c77baec89cb57374842abf733 parent d78bfd3ddae9c422dd350159110f9c4d7cfc50de author Liu Hong <hong.liu@intel.com> 1124446520 -0500 committer James Ketrenos <jketreno@linux.intel.com> 1127313183 -0500 Fix TKIP, repeated fragmentation problem, and payload_size reporting 1. TKIP encryption Originally, TKIP encryption issues msdu + mpdu encryption on every fragment. Change the behavior to msdu encryption on the whole packet, then mpdu encryption on every fragment. 2. Avoid repeated fragmentation when !host_encrypt. We only need do fragmentation when using host encryption. Otherwise we only need pass the whole packet to driver, letting driver do the fragmentation. 3. change the txb->payload_size to correct value FW will use this value to determine whether to do fragmentation. If we pass the wrong value, fw may cut on the wrong bound which will make decryption fail when we do host encryption. NOTE: This requires changing drivers (hostap) that have extra_prefix_len used within them (structure member name change). Signed-off-by: Hong Liu <liu.hong@intel.com> Signed-off-by: James Ketrenos <jketreno@linux.intel.com> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'net/ieee80211')
-rw-r--r--net/ieee80211/ieee80211_crypt.c4
-rw-r--r--net/ieee80211/ieee80211_crypt_ccmp.c4
-rw-r--r--net/ieee80211/ieee80211_crypt_tkip.c5
-rw-r--r--net/ieee80211/ieee80211_crypt_wep.c4
-rw-r--r--net/ieee80211/ieee80211_module.c7
-rw-r--r--net/ieee80211/ieee80211_tx.c148
6 files changed, 98 insertions, 74 deletions
diff --git a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c
index 60d3166facce..e26bcc918032 100644
--- a/net/ieee80211/ieee80211_crypt.c
+++ b/net/ieee80211/ieee80211_crypt.c
@@ -221,8 +221,8 @@ static struct ieee80211_crypto_ops ieee80211_crypt_null = {
221 .decrypt_msdu = NULL, 221 .decrypt_msdu = NULL,
222 .set_key = NULL, 222 .set_key = NULL,
223 .get_key = NULL, 223 .get_key = NULL,
224 .extra_prefix_len = 0, 224 .extra_mpdu_prefix_len = 0,
225 .extra_postfix_len = 0, 225 .extra_mpdu_postfix_len = 0,
226 .owner = THIS_MODULE, 226 .owner = THIS_MODULE,
227}; 227};
228 228
diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c
index d3b5cdee69ef..a3dc5712b98b 100644
--- a/net/ieee80211/ieee80211_crypt_ccmp.c
+++ b/net/ieee80211/ieee80211_crypt_ccmp.c
@@ -436,8 +436,8 @@ static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = {
436 .set_key = ieee80211_ccmp_set_key, 436 .set_key = ieee80211_ccmp_set_key,
437 .get_key = ieee80211_ccmp_get_key, 437 .get_key = ieee80211_ccmp_get_key,
438 .print_stats = ieee80211_ccmp_print_stats, 438 .print_stats = ieee80211_ccmp_print_stats,
439 .extra_prefix_len = CCMP_HDR_LEN, 439 .extra_mpdu_prefix_len = CCMP_HDR_LEN,
440 .extra_postfix_len = CCMP_MIC_LEN, 440 .extra_mpdu_postfix_len = CCMP_MIC_LEN,
441 .owner = THIS_MODULE, 441 .owner = THIS_MODULE,
442}; 442};
443 443
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
index f091aacd4297..f973d6cb8248 100644
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ b/net/ieee80211/ieee80211_crypt_tkip.c
@@ -690,8 +690,9 @@ static struct ieee80211_crypto_ops ieee80211_crypt_tkip = {
690 .set_key = ieee80211_tkip_set_key, 690 .set_key = ieee80211_tkip_set_key,
691 .get_key = ieee80211_tkip_get_key, 691 .get_key = ieee80211_tkip_get_key,
692 .print_stats = ieee80211_tkip_print_stats, 692 .print_stats = ieee80211_tkip_print_stats,
693 .extra_prefix_len = 4 + 4, /* IV + ExtIV */ 693 .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
694 .extra_postfix_len = 8 + 4, /* MIC + ICV */ 694 .extra_mpdu_postfix_len = 4, /* ICV */
695 .extra_msdu_postfix_len = 8, /* MIC */
695 .owner = THIS_MODULE, 696 .owner = THIS_MODULE,
696}; 697};
697 698
diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c
index 63e783fa5173..2aaeac1e02d7 100644
--- a/net/ieee80211/ieee80211_crypt_wep.c
+++ b/net/ieee80211/ieee80211_crypt_wep.c
@@ -239,8 +239,8 @@ static struct ieee80211_crypto_ops ieee80211_crypt_wep = {
239 .set_key = prism2_wep_set_key, 239 .set_key = prism2_wep_set_key,
240 .get_key = prism2_wep_get_key, 240 .get_key = prism2_wep_get_key,
241 .print_stats = prism2_wep_print_stats, 241 .print_stats = prism2_wep_print_stats,
242 .extra_prefix_len = 4, /* IV */ 242 .extra_mpdu_prefix_len = 4, /* IV */
243 .extra_postfix_len = 4, /* ICV */ 243 .extra_mpdu_postfix_len = 4, /* ICV */
244 .owner = THIS_MODULE, 244 .owner = THIS_MODULE,
245}; 245};
246 246
diff --git a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c
index 67d6bdd2e3f2..dddc61647390 100644
--- a/net/ieee80211/ieee80211_module.c
+++ b/net/ieee80211/ieee80211_module.c
@@ -133,6 +133,12 @@ struct net_device *alloc_ieee80211(int sizeof_priv)
133 /* Default to enabling full open WEP with host based encrypt/decrypt */ 133 /* Default to enabling full open WEP with host based encrypt/decrypt */
134 ieee->host_encrypt = 1; 134 ieee->host_encrypt = 1;
135 ieee->host_decrypt = 1; 135 ieee->host_decrypt = 1;
136 /* Host fragementation in Open mode. Default is enabled.
137 * Note: host fragmentation is always enabled if host encryption
138 * is enabled. For cards can do hardware encryption, they must do
139 * hardware fragmentation as well. So we don't need a variable
140 * like host_enc_frag. */
141 ieee->host_open_frag = 1;
136 ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ 142 ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
137 143
138 INIT_LIST_HEAD(&ieee->crypt_deinit_list); 144 INIT_LIST_HEAD(&ieee->crypt_deinit_list);
@@ -147,7 +153,6 @@ struct net_device *alloc_ieee80211(int sizeof_priv)
147 ieee->tkip_countermeasures = 0; 153 ieee->tkip_countermeasures = 0;
148 ieee->drop_unencrypted = 0; 154 ieee->drop_unencrypted = 0;
149 ieee->privacy_invoked = 0; 155 ieee->privacy_invoked = 0;
150 ieee->ieee802_1x = 1;
151 156
152 return dev; 157 return dev;
153 158
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index f505aa127e21..23a1f88de7cb 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -128,7 +128,7 @@ payload of each frame is reduced to 492 bytes.
128static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; 128static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
129static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; 129static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
130 130
131static inline int ieee80211_put_snap(u8 * data, u16 h_proto) 131static inline int ieee80211_copy_snap(u8 * data, u16 h_proto)
132{ 132{
133 struct ieee80211_snap_hdr *snap; 133 struct ieee80211_snap_hdr *snap;
134 u8 *oui; 134 u8 *oui;
@@ -159,15 +159,9 @@ static inline int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
159 159
160 /* To encrypt, frame format is: 160 /* To encrypt, frame format is:
161 * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ 161 * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
162
163 // PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption.
164 /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
165 * call both MSDU and MPDU encryption functions from here. */
166 atomic_inc(&crypt->refcnt); 162 atomic_inc(&crypt->refcnt);
167 res = 0; 163 res = 0;
168 if (crypt->ops->encrypt_msdu) 164 if (crypt->ops->encrypt_mpdu)
169 res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv);
170 if (res == 0 && crypt->ops->encrypt_mpdu)
171 res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); 165 res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
172 166
173 atomic_dec(&crypt->refcnt); 167 atomic_dec(&crypt->refcnt);
@@ -222,7 +216,7 @@ static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size,
222 return txb; 216 return txb;
223} 217}
224 218
225/* Incoming skb is converted to a txb which consist of 219/* Incoming skb is converted to a txb which consists of
226 * a block of 802.11 fragment packets (stored as skbs) */ 220 * a block of 802.11 fragment packets (stored as skbs) */
227int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) 221int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
228{ 222{
@@ -233,7 +227,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
233 rts_required; 227 rts_required;
234 unsigned long flags; 228 unsigned long flags;
235 struct net_device_stats *stats = &ieee->stats; 229 struct net_device_stats *stats = &ieee->stats;
236 int ether_type, encrypt, host_encrypt; 230 int ether_type, encrypt, host_encrypt, host_encrypt_msdu;
237 int bytes, fc, hdr_len; 231 int bytes, fc, hdr_len;
238 struct sk_buff *skb_frag; 232 struct sk_buff *skb_frag;
239 struct ieee80211_hdr_3addr header = { /* Ensure zero initialized */ 233 struct ieee80211_hdr_3addr header = { /* Ensure zero initialized */
@@ -241,8 +235,8 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
241 .seq_ctl = 0 235 .seq_ctl = 0
242 }; 236 };
243 u8 dest[ETH_ALEN], src[ETH_ALEN]; 237 u8 dest[ETH_ALEN], src[ETH_ALEN];
244
245 struct ieee80211_crypt_data *crypt; 238 struct ieee80211_crypt_data *crypt;
239 int snapped = 0;
246 240
247 spin_lock_irqsave(&ieee->lock, flags); 241 spin_lock_irqsave(&ieee->lock, flags);
248 242
@@ -266,6 +260,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
266 encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) && 260 encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) &&
267 ieee->sec.encrypt; 261 ieee->sec.encrypt;
268 host_encrypt = ieee->host_encrypt && encrypt; 262 host_encrypt = ieee->host_encrypt && encrypt;
263 host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt;
269 264
270 if (!encrypt && ieee->ieee802_1x && 265 if (!encrypt && ieee->ieee802_1x &&
271 ieee->drop_unencrypted && ether_type != ETH_P_PAE) { 266 ieee->drop_unencrypted && ether_type != ETH_P_PAE) {
@@ -291,14 +286,12 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
291 286
292 if (ieee->iw_mode == IW_MODE_INFRA) { 287 if (ieee->iw_mode == IW_MODE_INFRA) {
293 fc |= IEEE80211_FCTL_TODS; 288 fc |= IEEE80211_FCTL_TODS;
294 /* To DS: Addr1 = BSSID, Addr2 = SA, 289 /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
295 Addr3 = DA */
296 memcpy(header.addr1, ieee->bssid, ETH_ALEN); 290 memcpy(header.addr1, ieee->bssid, ETH_ALEN);
297 memcpy(header.addr2, src, ETH_ALEN); 291 memcpy(header.addr2, src, ETH_ALEN);
298 memcpy(header.addr3, dest, ETH_ALEN); 292 memcpy(header.addr3, dest, ETH_ALEN);
299 } else if (ieee->iw_mode == IW_MODE_ADHOC) { 293 } else if (ieee->iw_mode == IW_MODE_ADHOC) {
300 /* not From/To DS: Addr1 = DA, Addr2 = SA, 294 /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
301 Addr3 = BSSID */
302 memcpy(header.addr1, dest, ETH_ALEN); 295 memcpy(header.addr1, dest, ETH_ALEN);
303 memcpy(header.addr2, src, ETH_ALEN); 296 memcpy(header.addr2, src, ETH_ALEN);
304 memcpy(header.addr3, ieee->bssid, ETH_ALEN); 297 memcpy(header.addr3, ieee->bssid, ETH_ALEN);
@@ -306,42 +299,75 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
306 header.frame_ctl = cpu_to_le16(fc); 299 header.frame_ctl = cpu_to_le16(fc);
307 hdr_len = IEEE80211_3ADDR_LEN; 300 hdr_len = IEEE80211_3ADDR_LEN;
308 301
309 /* Determine fragmentation size based on destination (multicast 302 /* Encrypt msdu first on the whole data packet. */
310 * and broadcast are not fragmented) */ 303 if ((host_encrypt || host_encrypt_msdu) &&
311 if (is_multicast_ether_addr(dest) || is_broadcast_ether_addr(dest)) 304 crypt && crypt->ops && crypt->ops->encrypt_msdu) {
312 frag_size = MAX_FRAG_THRESHOLD; 305 int res = 0;
313 else 306 int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len +
314 frag_size = ieee->fts; 307 crypt->ops->extra_msdu_postfix_len;
308 struct sk_buff *skb_new = dev_alloc_skb(len);
309 if (unlikely(!skb_new))
310 goto failed;
311 skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len);
312 memcpy(skb_put(skb_new, hdr_len), &header, hdr_len);
313 snapped = 1;
314 ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
315 ether_type);
316 memcpy(skb_put(skb_new, skb->len), skb->data, skb->len);
317 res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
318 if (res < 0) {
319 IEEE80211_ERROR("msdu encryption failed\n");
320 dev_kfree_skb_any(skb_new);
321 goto failed;
322 }
323 dev_kfree_skb_any(skb);
324 skb = skb_new;
325 bytes += crypt->ops->extra_msdu_prefix_len +
326 crypt->ops->extra_msdu_postfix_len;
327 skb_pull(skb, hdr_len);
328 }
315 329
316 /* Determine amount of payload per fragment. Regardless of if 330 if (host_encrypt || ieee->host_open_frag) {
317 * this stack is providing the full 802.11 header, one will 331 /* Determine fragmentation size based on destination (multicast
318 * eventually be affixed to this fragment -- so we must account for 332 * and broadcast are not fragmented) */
319 * it when determining the amount of payload space. */ 333 if (is_multicast_ether_addr(dest))
320 bytes_per_frag = frag_size - IEEE80211_3ADDR_LEN; 334 frag_size = MAX_FRAG_THRESHOLD;
321 if (ieee->config & 335 else
322 (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) 336 frag_size = ieee->fts;
323 bytes_per_frag -= IEEE80211_FCS_LEN; 337
338 /* Determine amount of payload per fragment. Regardless of if
339 * this stack is providing the full 802.11 header, one will
340 * eventually be affixed to this fragment -- so we must account
341 * for it when determining the amount of payload space. */
342 bytes_per_frag = frag_size - IEEE80211_3ADDR_LEN;
343 if (ieee->config &
344 (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
345 bytes_per_frag -= IEEE80211_FCS_LEN;
324 346
325 /* Each fragment may need to have room for encryptiong pre/postfix */ 347 /* Each fragment may need to have room for encryptiong
326 if (host_encrypt) 348 * pre/postfix */
327 bytes_per_frag -= crypt->ops->extra_prefix_len + 349 if (host_encrypt)
328 crypt->ops->extra_postfix_len; 350 bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
329 351 crypt->ops->extra_mpdu_postfix_len;
330 /* Number of fragments is the total bytes_per_frag / 352
331 * payload_per_fragment */ 353 /* Number of fragments is the total
332 nr_frags = bytes / bytes_per_frag; 354 * bytes_per_frag / payload_per_fragment */
333 bytes_last_frag = bytes % bytes_per_frag; 355 nr_frags = bytes / bytes_per_frag;
334 if (bytes_last_frag) 356 bytes_last_frag = bytes % bytes_per_frag;
335 nr_frags++; 357 if (bytes_last_frag)
336 else 358 nr_frags++;
337 bytes_last_frag = bytes_per_frag; 359 else
360 bytes_last_frag = bytes_per_frag;
361 } else {
362 nr_frags = 1;
363 bytes_per_frag = bytes_last_frag = bytes;
364 frag_size = bytes + IEEE80211_3ADDR_LEN;
365 }
338 366
339 rts_required = (frag_size > ieee->rts 367 rts_required = (frag_size > ieee->rts
340 && ieee->config & CFG_IEEE80211_RTS); 368 && ieee->config & CFG_IEEE80211_RTS);
341 if (rts_required) 369 if (rts_required)
342 nr_frags++; 370 nr_frags++;
343 else
344 bytes_last_frag = bytes_per_frag;
345 371
346 /* When we allocate the TXB we allocate enough space for the reserve 372 /* When we allocate the TXB we allocate enough space for the reserve
347 * and full fragment bytes (bytes_per_frag doesn't include prefix, 373 * and full fragment bytes (bytes_per_frag doesn't include prefix,
@@ -353,7 +379,11 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
353 goto failed; 379 goto failed;
354 } 380 }
355 txb->encrypted = encrypt; 381 txb->encrypted = encrypt;
356 txb->payload_size = bytes; 382 if (host_encrypt)
383 txb->payload_size = frag_size * (nr_frags - 1) +
384 bytes_last_frag;
385 else
386 txb->payload_size = bytes;
357 387
358 if (rts_required) { 388 if (rts_required) {
359 skb_frag = txb->fragments[0]; 389 skb_frag = txb->fragments[0];
@@ -385,7 +415,8 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
385 skb_frag = txb->fragments[i]; 415 skb_frag = txb->fragments[i];
386 416
387 if (host_encrypt) 417 if (host_encrypt)
388 skb_reserve(skb_frag, crypt->ops->extra_prefix_len); 418 skb_reserve(skb_frag,
419 crypt->ops->extra_mpdu_prefix_len);
389 420
390 frag_hdr = 421 frag_hdr =
391 (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len); 422 (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len);
@@ -402,11 +433,10 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
402 bytes = bytes_last_frag; 433 bytes = bytes_last_frag;
403 } 434 }
404 435
405 /* Put a SNAP header on the first fragment */ 436 if (i == 0 && !snapped) {
406 if (i == 0) { 437 ieee80211_copy_snap(skb_put
407 ieee80211_put_snap(skb_put 438 (skb_frag, SNAP_SIZE + sizeof(u16)),
408 (skb_frag, SNAP_SIZE + sizeof(u16)), 439 ether_type);
409 ether_type);
410 bytes -= SNAP_SIZE + sizeof(u16); 440 bytes -= SNAP_SIZE + sizeof(u16);
411 } 441 }
412 442
@@ -420,19 +450,6 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
420 if (host_encrypt) 450 if (host_encrypt)
421 ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); 451 ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
422 452
423 /* ipw2200/2915 Hardware encryption doesn't support TKIP MIC */
424 if (!ieee->host_encrypt && encrypt &&
425 (ieee->sec.level == SEC_LEVEL_2) &&
426 crypt && crypt->ops && crypt->ops->encrypt_msdu) {
427 int res = 0;
428 res = crypt->ops->encrypt_msdu(skb_frag, hdr_len,
429 crypt->priv);
430 if (res < 0) {
431 IEEE80211_ERROR("TKIP MIC encryption failed\n");
432 goto failed;
433 }
434 }
435
436 if (ieee->config & 453 if (ieee->config &
437 (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) 454 (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
438 skb_put(skb_frag, 4); 455 skb_put(skb_frag, 4);
@@ -444,7 +461,8 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
444 dev_kfree_skb_any(skb); 461 dev_kfree_skb_any(skb);
445 462
446 if (txb) { 463 if (txb) {
447 if ((*ieee->hard_start_xmit) (txb, dev) == 0) { 464 int ret = (*ieee->hard_start_xmit) (txb, dev);
465 if (ret == 0) {
448 stats->tx_packets++; 466 stats->tx_packets++;
449 stats->tx_bytes += txb->payload_size; 467 stats->tx_bytes += txb->payload_size;
450 return 0; 468 return 0;