aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic-2.6
diff options
context:
space:
mode:
authorGabor Juhos <juhosg@openwrt.org>2009-04-06 18:08:48 +0000
committerGabor Juhos <juhosg@openwrt.org>2009-04-06 18:08:48 +0000
commit1dfa23a5c0a9b35ccac85e60567be49f76ff41cf (patch)
treea89f2e4206f64b64aa671e0ed92352a80ebacf3f /target/linux/generic-2.6
parent038b50e6976099fd30d0f7b78cfa2e12002d4ed6 (diff)
downloadupstream-1dfa23a5c0a9b35ccac85e60567be49f76ff41cf.tar.gz
upstream-1dfa23a5c0a9b35ccac85e60567be49f76ff41cf.tar.bz2
upstream-1dfa23a5c0a9b35ccac85e60567be49f76ff41cf.zip
[kernel] 2.6.28: add Kconfig prompt for the old crypto symbols
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15122 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic-2.6')
-rw-r--r--target/linux/generic-2.6/config-2.6.285
-rw-r--r--target/linux/generic-2.6/patches-2.6.28/979-crypto_add_kconfig_prompts.patch47
2 files changed, 50 insertions, 2 deletions
diff --git a/target/linux/generic-2.6/config-2.6.28 b/target/linux/generic-2.6/config-2.6.28
index c5e2a19263..262bec2998 100644
--- a/target/linux/generic-2.6/config-2.6.28
+++ b/target/linux/generic-2.6/config-2.6.28
@@ -263,7 +263,7 @@ CONFIG_CRC32=y
# CONFIG_CRC_T10DIF is not set
CONFIG_CROSSCOMPILE=y
CONFIG_CRYPTO=y
-# CONFIG_CRYPTO_AEAD is not set
+CONFIG_CRYPTO_AEAD=m
CONFIG_CRYPTO_AEAD2=m
CONFIG_CRYPTO_AES=m
CONFIG_CRYPTO_ALGAPI=m
@@ -292,7 +292,7 @@ CONFIG_CRYPTO_ECB=m
# CONFIG_CRYPTO_FIPS is not set
# CONFIG_CRYPTO_GCM is not set
# CONFIG_CRYPTO_GF128MUL is not set
-# CONFIG_CRYPTO_HASH is not set
+CONFIG_CRYPTO_HASH=m
CONFIG_CRYPTO_HASH2=m
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_HW is not set
@@ -311,6 +311,7 @@ CONFIG_CRYPTO_MANAGER2=m
# CONFIG_CRYPTO_RMD160 is not set
# CONFIG_CRYPTO_RMD256 is not set
# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_RNG=m
CONFIG_CRYPTO_RNG2=m
# CONFIG_CRYPTO_SALSA20 is not set
# CONFIG_CRYPTO_SEED is not set
diff --git a/target/linux/generic-2.6/patches-2.6.28/979-crypto_add_kconfig_prompts.patch b/target/linux/generic-2.6/patches-2.6.28/979-crypto_add_kconfig_prompts.patch
new file mode 100644
index 0000000000..df7837dbab
--- /dev/null
+++ b/target/linux/generic-2.6/patches-2.6.28/979-crypto_add_kconfig_prompts.patch
@@ -0,0 +1,47 @@
+--- a/crypto/Kconfig
++++ b/crypto/Kconfig
+@@ -30,7 +30,7 @@ config CRYPTO_FIPS
+ this is.
+
+ config CRYPTO_ALGAPI
+- tristate
++ tristate "ALGAPI"
+ select CRYPTO_ALGAPI2
+ help
+ This option provides the API for cryptographic algorithms.
+@@ -39,7 +39,7 @@ config CRYPTO_ALGAPI2
+ tristate
+
+ config CRYPTO_AEAD
+- tristate
++ tristate "AEAD"
+ select CRYPTO_AEAD2
+ select CRYPTO_ALGAPI
+
+@@ -48,7 +48,7 @@ config CRYPTO_AEAD2
+ select CRYPTO_ALGAPI2
+
+ config CRYPTO_BLKCIPHER
+- tristate
++ tristate "BLKCIPHER"
+ select CRYPTO_BLKCIPHER2
+ select CRYPTO_ALGAPI
+
+@@ -58,7 +58,7 @@ config CRYPTO_BLKCIPHER2
+ select CRYPTO_RNG2
+
+ config CRYPTO_HASH
+- tristate
++ tristate "HASH"
+ select CRYPTO_HASH2
+ select CRYPTO_ALGAPI
+
+@@ -67,7 +67,7 @@ config CRYPTO_HASH2
+ select CRYPTO_ALGAPI2
+
+ config CRYPTO_RNG
+- tristate
++ tristate "RNG"
+ select CRYPTO_RNG2
+ select CRYPTO_ALGAPI
+
href='#n283'>283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
@@ -124,7 +124,7 @@ enum {
 };
 
 static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
-	const char *, int, int, struct net_device *);
+	const char *, int, int, struct net_device *, struct ieee80211vap *);
 static void ath_vap_delete(struct ieee80211vap *);
 static int ath_init(struct net_device *);
 static int ath_set_ack_bitrate(struct ath_softc *, int);
@@ -1123,8 +1123,6 @@ ath_attach(u_int16_t devid, struct net_d
 			autocreatemode = IEEE80211_M_IBSS;
 		else if (!strcmp(autocreate, "ahdemo"))
 			autocreatemode = IEEE80211_M_AHDEMO;
-		else if (!strcmp(autocreate, "wds"))
-			autocreatemode = IEEE80211_M_WDS;
 		else if (!strcmp(autocreate, "monitor"))
 			autocreatemode = IEEE80211_M_MONITOR;
 		else {
@@ -1137,7 +1135,7 @@ ath_attach(u_int16_t devid, struct net_d
 	if (autocreatemode != -1) {
 		rtnl_lock();
 		vap = ieee80211_create_vap(ic, "ath%d", dev,
-				autocreatemode, 0);
+				autocreatemode, 0, NULL);
 		rtnl_unlock();
 		if (vap == NULL)
 			EPRINTF(sc, "Autocreation of %s VAP failed.", autocreate);
@@ -1230,14 +1228,14 @@ ath_detach(struct net_device *dev)
 
 static struct ieee80211vap *
 ath_vap_create(struct ieee80211com *ic, const char *name,
-	int opmode, int flags, struct net_device *mdev)
+	int opmode, int flags, struct net_device *mdev, struct ieee80211vap *master)
 {
 	struct ath_softc *sc = ic->ic_dev->priv;
 	struct ath_hal *ah = sc->sc_ah;
 	struct net_device *dev;
 	struct ath_vap *avp;
 	struct ieee80211vap *vap;
-	int ic_opmode;
+	int ic_opmode = IEEE80211_M_STA;
 
 	if (ic->ic_dev->flags & IFF_RUNNING) {
 		/* needs to disable hardware too */
@@ -1271,8 +1269,12 @@ ath_vap_create(struct ieee80211com *ic, 
 		} else
 			ic_opmode = opmode;
 		break;
-	case IEEE80211_M_HOSTAP:
 	case IEEE80211_M_WDS:
+		if (!master)
+			return NULL;
+		ic_opmode = ic->ic_opmode;
+		break;
+	case IEEE80211_M_HOSTAP:
 		/* permit multiple APs and/or WDS links */
 		/* XXX sta+ap for repeater/bridge application */
 		if ((sc->sc_nvaps != 0) && (ic->ic_opmode == IEEE80211_M_STA))
@@ -1304,7 +1306,7 @@ ath_vap_create(struct ieee80211com *ic, 
 	}
 
 	avp = dev->priv;
-	ieee80211_vap_setup(ic, dev, name, opmode, flags);
+	ieee80211_vap_setup(ic, dev, name, opmode, flags, master);
 	/* override with driver methods */
 	vap = &avp->av_vap;
 	avp->av_newstate = vap->iv_newstate;
@@ -4209,8 +4211,7 @@ ath_calcrxfilter(struct ath_softc *sc)
 	if (ic->ic_opmode == IEEE80211_M_STA ||
 	    sc->sc_opmode == HAL_M_IBSS ||	/* NB: AHDEMO too */
 	    (sc->sc_nostabeacons) || sc->sc_scanning ||
-		((ic->ic_opmode == IEEE80211_M_HOSTAP) &&
-		 (ic->ic_protmode != IEEE80211_PROT_NONE)))
+		(ic->ic_opmode == IEEE80211_M_HOSTAP))
 		rfilt |= HAL_RX_FILTER_BEACON;
 	if (sc->sc_nmonvaps > 0)
 		rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON |
@@ -9030,8 +9031,6 @@ ath_calibrate(unsigned long arg)
 		 * set sc->beacons if we might need to restart
                  * them after ath_reset. */
 		if (!sc->sc_beacons &&
-				(TAILQ_FIRST(&ic->ic_vaps)->iv_opmode != 
-				 IEEE80211_M_WDS) &&
 				!txcont_was_active &&
 				!sc->sc_dfs_cac) {
 			sc->sc_beacons = 1;
--- a/net80211/ieee80211.c
+++ b/net80211/ieee80211.c
@@ -396,7 +396,7 @@ EXPORT_SYMBOL(ieee80211_ifdetach);
 
 int
 ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
-	const char *name, int opmode, int flags)
+	const char *name, int opmode, int flags, struct ieee80211vap *master)
 {
 #define	IEEE80211_C_OPMODE \
 	(IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
@@ -510,9 +510,18 @@ ieee80211_vap_setup(struct ieee80211com 
 
 	vap->iv_monitor_crc_errors = 0;
 	vap->iv_monitor_phy_errors = 0;
+	TAILQ_INIT(&vap->iv_wdslinks);
 
-	IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr);
-	IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr);
+	if (master && (vap->iv_opmode == IEEE80211_M_WDS)) {
+		vap->iv_master = master;
+		TAILQ_INSERT_TAIL(&master->iv_wdslinks, vap, iv_wdsnext);
+		/* use the same BSSID as the master interface */
+		IEEE80211_ADDR_COPY(vap->iv_myaddr, vap->iv_master->iv_myaddr);
+		IEEE80211_ADDR_COPY(vap->iv_bssid, vap->iv_master->iv_myaddr);
+	} else {
+		IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr);
+		IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr);
+	}
 	/* NB: Defer setting dev_addr so driver can override */
 
 	ieee80211_crypto_vattach(vap);
@@ -547,7 +556,8 @@ ieee80211_vap_attach(struct ieee80211vap
 	ifmedia_set(&vap->iv_media, imr.ifm_active);
 
 	IEEE80211_LOCK_IRQ(ic);
-	TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
+	if (vap->iv_opmode != IEEE80211_M_WDS)
+		TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
 	IEEE80211_UNLOCK_IRQ(ic);
 
 	IEEE80211_ADDR_COPY(dev->dev_addr, vap->iv_myaddr);
@@ -579,10 +589,24 @@ ieee80211_vap_detach(struct ieee80211vap
 {
 	struct ieee80211com *ic = vap->iv_ic;
 	struct net_device *dev = vap->iv_dev;
+	struct ieee80211vap *avp;
+
+	/* Drop all WDS links that belong to this vap */
+	while ((avp = TAILQ_FIRST(&vap->iv_wdslinks)) != NULL) {
+		ieee80211_stop(avp->iv_dev);
+		ic->ic_vap_delete(avp);
+	}
 
 	IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
 	IEEE80211_LOCK_IRQ(ic);
-	TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
+	if (vap->iv_wdsnode)
+		ieee80211_unref_node(&vap->iv_wdsnode);
+	if ((vap->iv_opmode == IEEE80211_M_WDS) &&
+		(vap->iv_master != NULL))
+		TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
+	else
+		TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
+
 	if (TAILQ_EMPTY(&ic->ic_vaps))		/* reset to supported mode */
 		ic->ic_opmode = IEEE80211_M_STA;
 	IEEE80211_UNLOCK_IRQ(ic);
--- a/net80211/ieee80211_ioctl.h
+++ b/net80211/ieee80211_ioctl.h
@@ -474,7 +474,7 @@ struct ieee80211req {
 #define	IEEE80211_IOC_DTIM_PERIOD	52	/* DTIM period (beacons) */
 #define	IEEE80211_IOC_BEACON_INTERVAL	53	/* beacon interval (ms) */
 #define	IEEE80211_IOC_ADDMAC		54	/* add sta to MAC ACL table */
-#define	IEEE80211_IOC_DELMAC		55	/* del sta from MAC ACL table */
+#define	IEEE80211_IOC_SETMAC		55	/* set interface wds mac addr */
 #define	IEEE80211_IOC_FF		56	/* ATH fast frames (on, off) */
 #define	IEEE80211_IOC_TURBOP		57	/* ATH turbo' (on, off) */
 #define	IEEE80211_IOC_APPIEBUF		58	/* IE in the management frame */
@@ -552,8 +552,8 @@ struct ieee80211req_scan_result {
 #define	IEEE80211_IOCTL_HALMAP		(SIOCIWFIRSTPRIV+21)
 #define	IEEE80211_IOCTL_ADDMAC		(SIOCIWFIRSTPRIV+22)
 #define	IEEE80211_IOCTL_DELMAC		(SIOCIWFIRSTPRIV+24)
-#define	IEEE80211_IOCTL_WDSADDMAC	(SIOCIWFIRSTPRIV+26)
-#define	IEEE80211_IOCTL_WDSDELMAC	(SIOCIWFIRSTPRIV+28)
+#define	IEEE80211_IOCTL_WDSADDMAC	(SIOCIWFIRSTPRIV+25)
+#define	IEEE80211_IOCTL_WDSSETMAC	(SIOCIWFIRSTPRIV+26)
 #define	IEEE80211_IOCTL_KICKMAC		(SIOCIWFIRSTPRIV+30)
 #define	IEEE80211_IOCTL_SETSCANLIST	(SIOCIWFIRSTPRIV+31)
 
--- a/net80211/ieee80211_linux.h
+++ b/net80211/ieee80211_linux.h
@@ -650,5 +650,5 @@ struct ifreq;
 int ieee80211_ioctl_create_vap(struct ieee80211com *, struct ifreq *,
 	struct net_device *);
 struct ieee80211vap *ieee80211_create_vap(struct ieee80211com *, char *,
-	struct net_device *, int, int);
+	struct net_device *, int, int, struct ieee80211vap *);
 #endif /* _NET80211_IEEE80211_LINUX_H_ */
--- a/net80211/ieee80211_var.h
+++ b/net80211/ieee80211_var.h
@@ -187,6 +187,12 @@ struct ieee80211vap {
 	struct ieee80211_proc_entry *iv_proc_entries;
 	struct vlan_group *iv_vlgrp;			/* vlan group state */
 
+	/* list of wds links */
+	TAILQ_HEAD(, ieee80211vap) iv_wdslinks;
+	TAILQ_ENTRY(ieee80211vap) iv_wdsnext;
+	struct ieee80211vap *iv_master;
+	struct ieee80211_node *iv_wdsnode;
+
 	TAILQ_ENTRY(ieee80211vap) iv_next;		/* list of vap instances */
 	struct ieee80211com *iv_ic;			/* back ptr to common state */
 	u_int32_t iv_debug;				/* debug msg flags */
@@ -447,7 +453,7 @@ struct ieee80211com {
 	atomic_t ic_node_counter;
 	/* Virtual AP create/delete */
 	struct ieee80211vap *(*ic_vap_create)(struct ieee80211com *,
-		const char *, int, int, struct net_device *);
+		const char *, int, int, struct net_device *, struct ieee80211vap *);
 	void (*ic_vap_delete)(struct ieee80211vap *);
 
 	/* Send/recv 802.11 management frame */
@@ -703,7 +709,7 @@ MALLOC_DECLARE(M_80211_VAP);
 int ieee80211_ifattach(struct ieee80211com *);
 void ieee80211_ifdetach(struct ieee80211com *);
 int ieee80211_vap_setup(struct ieee80211com *, struct net_device *,
-	const char *, int, int);
+	const char *, int, int, struct ieee80211vap *);
 int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t);
 void ieee80211_vap_detach(struct ieee80211vap *);
 void ieee80211_mark_dfs(struct ieee80211com *, struct ieee80211_channel *);
--- a/net80211/ieee80211_wireless.c
+++ b/net80211/ieee80211_wireless.c
@@ -2190,7 +2190,7 @@ ieee80211_setupxr(struct ieee80211vap *v
 			ieee80211_scan_flush(ic);	/* NB: could optimize */
 
 			if (!(xrvap = ic->ic_vap_create(ic, name, IEEE80211_M_HOSTAP,
-				IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev)))
+				IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev, NULL)))
 				return;
 
 			/* We use iv_xrvap to link to the parent VAP as well */
@@ -3801,74 +3801,51 @@ ieee80211_ioctl_setmlme(struct net_devic
 	return 0;
 }
 
+#define WDSNAME ".wds%d"
 static int
-ieee80211_ioctl_wdsmac(struct net_device *dev, struct iw_request_info *info,
+ieee80211_ioctl_wdsaddmac(struct net_device *dev, struct iw_request_info *info,
 	void *w, char *extra)
 {
 	struct ieee80211vap *vap = dev->priv;
 	struct sockaddr *sa = (struct sockaddr *)extra;
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ieee80211vap *avp;
+	char *name;
 
-	if (!IEEE80211_ADDR_NULL(vap->wds_mac)) {
-		printk("%s: Failed to add WDS MAC: " MAC_FMT "\n", dev->name,
-			MAC_ADDR(sa->sa_data));
-		printk("%s: Device already has WDS mac address attached,"
-			" remove first\n", dev->name);
-		return -1;
-	}
-
-	memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN);
-
-	printk("%s: Added WDS MAC: " MAC_FMT "\n", dev->name,
-		MAC_ADDR(vap->wds_mac));
+	name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSNAME) + 1, GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
 
-	if (IS_UP(vap->iv_dev)) {
-		/* Force us back to scan state to force us to go back through RUN
-		 * state and create/pin the WDS peer node into memory. */
-		return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
-	}
+	strcpy(name, vap->iv_dev->name);
+	strcat(name, WDSNAME);
+	avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
+	kfree(name);
+	if (!avp)
+		return -ENOMEM;
 
+	memcpy(avp->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN);
 	return 0;
 }
+#undef WDSNAME
 
 static int
-ieee80211_ioctl_wdsdelmac(struct net_device *dev, struct iw_request_info *info,
+ieee80211_ioctl_wdssetmac(struct net_device *dev, struct iw_request_info *info,
 	void *w, char *extra)
 {
 	struct ieee80211vap *vap = dev->priv;
 	struct sockaddr *sa = (struct sockaddr *)extra;
-	struct ieee80211com *ic = vap->iv_ic;
-	struct ieee80211_node *wds_ni;
 
-	/* WDS Mac address filed already? */
-	if (IEEE80211_ADDR_NULL(vap->wds_mac))
-		return 0;
+	if (vap->iv_opmode != IEEE80211_M_WDS)
+		return -EINVAL;
 
-	/* Compare suplied MAC address with WDS MAC of this interface 
-	 * remove when mac address is known
-	 */
-	if (memcmp(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN) == 0) {
-		if (IS_UP(vap->iv_dev)) {
-			wds_ni = ieee80211_find_txnode(vap, vap->wds_mac);
-			if (wds_ni != NULL) {
-				/* Release reference created by find node */
-				ieee80211_unref_node(&wds_ni);
-				/* Release reference created by transition to RUN state,
-				 * [pinning peer node into the table] */
-				ieee80211_unref_node(&wds_ni);
-			}
-		}
-		memset(vap->wds_mac, 0x00, IEEE80211_ADDR_LEN);
-		if (IS_UP(vap->iv_dev)) {
-			/* This leaves a dead WDS node, until started again */
-			return ic->ic_reset(ic->ic_dev);
-		}
-		return 0;
+	memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN);
+	if (IS_UP(vap->iv_dev)) {
+		/* Force us back to scan state to force us to go back through RUN
+		 * state and create/pin the WDS peer node into memory. */
+		return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
 	}
 
-	printk("%s: WDS MAC address " MAC_FMT " is not known by this interface\n",
-		dev->name, MAC_ADDR(sa->sa_data));
-
-	return -1;
+	return 0;
 }
 
 /*
@@ -5391,8 +5368,8 @@ static const struct iw_priv_args ieee802
 	  IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac"},
 	{ IEEE80211_IOCTL_WDSADDMAC,
 	  IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_add" },
-	{ IEEE80211_IOCTL_WDSDELMAC,
-	  IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_del" },
+	{ IEEE80211_IOCTL_WDSSETMAC,
+	  IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_set" },
 	{ IEEE80211_IOCTL_SETCHANLIST,
 	  IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"setchanlist" },
 	{ IEEE80211_IOCTL_GETCHANLIST,
@@ -5884,8 +5861,8 @@ static const iw_handler ieee80211_priv_h
 #endif
 	set_priv(IEEE80211_IOCTL_ADDMAC, ieee80211_ioctl_addmac),
 	set_priv(IEEE80211_IOCTL_DELMAC, ieee80211_ioctl_delmac),
-	set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsmac),
-	set_priv(IEEE80211_IOCTL_WDSDELMAC, ieee80211_ioctl_wdsdelmac),
+	set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsaddmac),
+	set_priv(IEEE80211_IOCTL_WDSSETMAC, ieee80211_ioctl_wdssetmac),
 	set_priv(IEEE80211_IOCTL_KICKMAC, ieee80211_ioctl_kickmac),
 	set_priv(IEEE80211_IOCTL_SETSCANLIST, ieee80211_ioctl_setscanlist),
 #ifdef ATH_REVERSE_ENGINEERING
@@ -5956,7 +5933,7 @@ ieee80211_ioctl_create_vap(struct ieee80
 
 	strncpy(name, cp.icp_name, sizeof(name));
 
-	vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags);
+	vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags, NULL);
 	if (vap == NULL)
 		return -EIO;
 
@@ -5973,9 +5950,9 @@ EXPORT_SYMBOL(ieee80211_ioctl_create_vap
  */
 struct ieee80211vap*
 ieee80211_create_vap(struct ieee80211com *ic, char *name,
-	struct net_device *mdev, int opmode, int opflags)
+	struct net_device *mdev, int opmode, int opflags, struct ieee80211vap *master)
 {
-	return ic->ic_vap_create(ic, name, opmode, opflags, mdev);
+	return ic->ic_vap_create(ic, name, opmode, opflags, mdev, master);
 }
 EXPORT_SYMBOL(ieee80211_create_vap);
 
--- a/net80211/ieee80211_input.c
+++ b/net80211/ieee80211_input.c
@@ -201,6 +201,7 @@ ieee80211_input(struct ieee80211vap * va
 	struct ieee80211_node * ni = ni_or_null;
 	struct ieee80211com *ic = vap->iv_ic;
 	struct net_device *dev = vap->iv_dev;
+	struct ieee80211_node *ni_wds = NULL;
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *key;
 	struct ether_header *eh;
@@ -545,11 +546,30 @@ ieee80211_input(struct ieee80211vap * va
 			 * the node table for the packet source address (addr4).
 			 * If not, add one.
 			 */
-			/* XXX: Useless node mgmt API; make better */
+
+			/* check for wds link first */
 			if (dir == IEEE80211_FC1_DIR_DSTODS) {
-				struct ieee80211_node_table *nt;
+				struct ieee80211vap *avp;
+
+				TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
+					if (!memcmp(avp->wds_mac, wh->i_addr2, IEEE80211_ADDR_LEN)) {
+						IEEE80211_LOCK_IRQ(ni->ni_ic);
+						ni_wds = avp->iv_wdsnode;
+						IEEE80211_UNLOCK_IRQ(ni->ni_ic);
+						break;
+					}
+				}
+				if (ni_wds != NULL) {
+					if (ni_or_null == NULL)
+						ieee80211_unref_node(&ni);
+					ni = ieee80211_ref_node(ni_wds);
+				}
+			}
+
+			/* XXX: Useless node mgmt API; make better */
+			if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds) {
+				struct ieee80211_node_table *nt = &ic->ic_sta;
 				struct ieee80211_frame_addr4 *wh4;
-				struct ieee80211_node *ni_wds;
 
 				if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) {
 					IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
@@ -557,7 +577,6 @@ ieee80211_input(struct ieee80211vap * va
 					goto err;
 				}
 				wh4 = (struct ieee80211_frame_addr4 *)skb->data;
-				nt = &ic->ic_sta;
 				ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4);
 				/* Last call increments ref count if !NULL */
 				if ((ni_wds != NULL) && (ni_wds != ni)) {
@@ -3084,8 +3103,7 @@ ieee80211_recv_mgmt(struct ieee80211vap 
 		    (vap->iv_opmode == IEEE80211_M_STA && ni->ni_associd) ||
 		    (vap->iv_opmode == IEEE80211_M_IBSS) ||
 			((subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
-			 (vap->iv_opmode == IEEE80211_M_HOSTAP) &&
-			 (ic->ic_protmode != IEEE80211_PROT_NONE)))) {
+			 (vap->iv_opmode == IEEE80211_M_HOSTAP)))) {
 			vap->iv_stats.is_rx_mgtdiscard++;
 			return;
 		}
@@ -3471,13 +3489,53 @@ ieee80211_recv_mgmt(struct ieee80211vap 
 		 */
 		if (ic->ic_flags & IEEE80211_F_SCAN) {
 			ieee80211_add_scan(vap, &scan, wh, subtype, rssi, rtsf);
-			return;
 		}
-		if ((vap->iv_opmode == IEEE80211_M_IBSS) && 
-				(scan.capinfo & IEEE80211_CAPINFO_IBSS)) {
+		/* NB: Behavior of WDS-Link and Ad-Hoc is very similar here:
+		 * When we receive a beacon that belongs to the AP that we're
+		 * connected to, use it to refresh the local node info.
+		 * If no node is found, go through the vap's wds link table
+		 * and try to find the sub-vap that is interested in this address
+		 */
+		if (((vap->iv_opmode == IEEE80211_M_IBSS) &&
+				(scan.capinfo & IEEE80211_CAPINFO_IBSS)) ||
+				(((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
+				 (vap->iv_opmode == IEEE80211_M_WDS)) &&
+				(scan.capinfo & IEEE80211_CAPINFO_ESS))) {
+			struct ieee80211vap *avp = NULL;
+
+			IEEE80211_LOCK_IRQ(vap->iv_ic);
+			if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+				int found = 0;
+
+				TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
+					if (!memcmp(avp->wds_mac, wh->i_addr2, IEEE80211_ADDR_LEN)) {
+						found = 1;
+						break;
+					}
+				}
+				if (found) {
+					if (!avp->iv_wdsnode)
+						break;
+					ni = ni_or_null = avp->iv_wdsnode;
+				} else {
+					avp = NULL;
+				}
+			}
+			IEEE80211_UNLOCK_IRQ(vap->iv_ic);
+
+
 			if (ni_or_null == NULL) {
-				/* Create a new entry in the neighbor table. */
-				ni = ieee80211_add_neighbor(vap, wh, &scan);
+				if (avp) {
+					IEEE80211_LOCK_IRQ(ic);
+					ni = ieee80211_add_neighbor(avp, wh, &scan);
+					/* force assoc */
+					ni->ni_associd |= 0xc000;
+					avp->iv_wdsnode = ieee80211_ref_node(ni);
+					IEEE80211_UNLOCK_IRQ(ic);
+				} else if (vap->iv_opmode == IEEE80211_M_IBSS) {
+					/* Create a new entry in the neighbor table. */
+					ni = ieee80211_add_neighbor(vap, wh, &scan);
+				}
 			} else {
 				/*
 				 * Copy data from beacon to neighbor table.
@@ -3490,6 +3548,7 @@ ieee80211_recv_mgmt(struct ieee80211vap 
 				IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
 				memcpy(ni->ni_tstamp.data, scan.tstamp,
 					sizeof(ni->ni_tstamp));
+				ni->ni_inact = ni->ni_inact_reload;
 				ni->ni_intval = 
 					IEEE80211_BINTVAL_SANITISE(scan.bintval);
 				ni->ni_capinfo = scan.capinfo;
--- a/net80211/ieee80211_node.c
+++ b/net80211/ieee80211_node.c
@@ -1553,22 +1553,24 @@ ieee80211_find_rxnode(struct ieee80211co
 	((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
 	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
+	const u_int8_t *addr;
+
+	if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+		addr = wh->i_addr1;
+	else
+		addr = wh->i_addr2;
+
+	if (IEEE80211_IS_MULTICAST(addr))
+		return NULL;
 
 	/* XXX check ic_bss first in station mode */
 	/* XXX 4-address frames? */
 	nt = &ic->ic_sta;
 	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
-	if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
 #ifdef IEEE80211_DEBUG_REFCNT
-		ni = ieee80211_find_node_locked_debug(nt, wh->i_addr1, func, line);
+	ni = ieee80211_find_node_locked_debug(nt, addr, func, line);
 #else
-		ni = ieee80211_find_node_locked(nt, wh->i_addr1);
-#endif
-	else
-#ifdef IEEE80211_DEBUG_REFCNT
-		ni = ieee80211_find_node_locked_debug(nt, wh->i_addr2, func, line);
-#else
-		ni = ieee80211_find_node_locked(nt, wh->i_addr2);
+	ni = ieee80211_find_node_locked(nt, addr);
 #endif
 	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
 
@@ -1669,6 +1671,11 @@ ieee80211_free_node(struct ieee80211_nod
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 
+	IEEE80211_LOCK_IRQ(ni->ni_ic);
+	if (vap && ni == vap->iv_wdsnode)
+		vap->iv_wdsnode = NULL;
+	IEEE80211_UNLOCK_IRQ(ni->ni_ic);
+
 	atomic_dec(&ni->ni_ic->ic_node_counter);
 	node_print_message(IEEE80211_MSG_NODE|IEEE80211_MSG_NODE_REF,
 			   1 /* show counter */, 
@@ -1781,22 +1788,6 @@ restart:
 		    jiffies > ni->ni_rxfragstamp + HZ) {
 			ieee80211_dev_kfree_skb(&ni->ni_rxfrag);
 		}
-		/*
-		 * Special case ourself; we may be idle for extended periods
-		 * of time and regardless reclaiming our state is wrong.
-		 * Special case a WDS link: it may be dead or idle, but it is 
-		 * never ok to reclaim it, as this will block transmissions
-		 * and nobody will recreate the node when the WDS peer is
-		 * available again. */
-		if ((ni == ni->ni_vap->iv_bss) ||
-		    (ni->ni_vap->iv_opmode == IEEE80211_M_WDS && 
-		     !memcmp(ni->ni_macaddr, ni->ni_vap->wds_mac, ETH_ALEN)))
-		{
-			/* NB: don't permit it to go negative */
-			if (ni->ni_inact > 0)
-				ni->ni_inact--;
-			continue;
-		}
 		ni->ni_inact--;
 		if (ni->ni_associd != 0 || isadhoc) {
 			struct ieee80211vap *vap = ni->ni_vap;
--- a/net80211/ieee80211_output.c
+++ b/net80211/ieee80211_output.c
@@ -246,10 +246,16 @@ ieee80211_hardstart(struct sk_buff *skb,
 	 * things like power save.
 	 */
 	eh = (struct ether_header *)skb->data;
-	if (vap->iv_opmode == IEEE80211_M_WDS)
-		ni = ieee80211_find_txnode(vap, vap->wds_mac);
-	else
+	if (vap->iv_opmode == IEEE80211_M_WDS) {
+		IEEE80211_LOCK_IRQ(ic);
+		ni = vap->iv_wdsnode;
+		IEEE80211_UNLOCK_IRQ(ic);
+		if (!ni)
+			goto bad;
+		ni = ieee80211_ref_node(vap->iv_wdsnode);
+	} else {
 		ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+	}
 	if (ni == NULL) {
 		/* NB: ieee80211_find_txnode does stat+msg */
 		goto bad;
@@ -788,7 +794,7 @@ ieee80211_encap(struct ieee80211_node *n
 		break;
 	case IEEE80211_M_WDS:
 		use4addr = 1;
-		ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr);
+		ismulticast = 0;
 		break;
 	case IEEE80211_M_HOSTAP:
 		if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) &&
@@ -973,7 +979,7 @@ ieee80211_encap(struct ieee80211_node *n
 			break;
 		case IEEE80211_M_WDS:
 			wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
-			IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+			IEEE80211_ADDR_COPY(wh->i_addr1, vap->wds_mac);
 			IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
 			IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
 			IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
--- a/tools/athkey.c
+++ b/tools/athkey.c
@@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo
 				IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
 				IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
 				IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
-				IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
+				IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC),
 				IOCTL_ERR(IEEE80211_IOCTL_READREG),
 				IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
 			};
--- a/tools/athchans.c
+++ b/tools/athchans.c
@@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo
 				IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
 				IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
 				IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
-				IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
+				IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC),
 				IOCTL_ERR(IEEE80211_IOCTL_READREG),
 				IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
 			};
--- a/tools/wlanconfig.c
+++ b/tools/wlanconfig.c
@@ -968,7 +968,7 @@ do80211priv(struct iwreq *iwr, const cha
 			IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
 			IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
 			IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
-			IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
+			IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC),
 			IOCTL_ERR(IEEE80211_IOCTL_READREG),
 			IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
 		};
--- a/net80211/ieee80211_proto.c
+++ b/net80211/ieee80211_proto.c
@@ -1557,57 +1557,12 @@ __ieee80211_newstate(struct ieee80211vap
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 			if (vap->iv_opmode == IEEE80211_M_MONITOR ||
-			    vap->iv_opmode == IEEE80211_M_WDS ||
 			    vap->iv_opmode == IEEE80211_M_HOSTAP) {
 				/*
 				 * Already have a channel; bypass the
 				 * scan and startup immediately.
 				 */
 				ieee80211_create_ibss(vap, ic->ic_curchan);
-
-				/* In WDS mode, allocate and initialize peer node. */
-				if (vap->iv_opmode == IEEE80211_M_WDS) {
-					/* XXX: This is horribly non-atomic. */
-					struct ieee80211_node *wds_ni =
-						ieee80211_find_node(&ic->ic_sta,
-								vap->wds_mac);
-
-					if (wds_ni == NULL) {
-						wds_ni = ieee80211_alloc_node_table(
-								vap,
-								vap->wds_mac);
-						if (wds_ni != NULL) {
-							ieee80211_add_wds_addr(
-									&ic->ic_sta,
-									wds_ni,
-									vap->wds_mac,
-									1);
-							ieee80211_ref_node(wds_ni); /* pin in memory */
-						}
-						else
-							IEEE80211_DPRINTF(
-									vap,
-									IEEE80211_MSG_NODE,
-									"%s: Unable to "
-									"allocate node for "
-									"WDS: " MAC_FMT "\n",
-									__func__,
-									MAC_ADDR(
-										vap->wds_mac)
-									);
-					}
-
-					if (wds_ni != NULL) {
-						ieee80211_node_authorize(wds_ni);
-						wds_ni->ni_chan =
-							vap->iv_bss->ni_chan;
-						wds_ni->ni_capinfo =
-							ni->ni_capinfo;
-						wds_ni->ni_associd = 1;
-						wds_ni->ni_ath_flags =
-							vap->iv_ath_cap;
-					}
-				}
 				break;
 			}
 			/* fall thru... */