11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * llc_station.c - station component of LLC
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1997 by Procom Technology, Inc.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 		 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program can be redistributed or modified under the terms of the
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License as published by the Free Software Foundation.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed without any warranty or implied warranty
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of merchantability or fitness for a particular purpose.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * See the GNU General Public License for more details.
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
165a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_sap.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_conn.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_c_ac.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_s_ac.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_c_ev.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_c_st.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_s_ev.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_s_st.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/llc_pdu.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
32025e36332573177ecd7c12730e18a4390f994f05Ben Hutchings	return LLC_PDU_IS_CMD(pdu) &&			/* command PDU */
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       LLC_PDU_TYPE_IS_U(pdu) &&		/* U type PDU */
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       !pdu->dsap ? 0 : 1;			/* NULL DSAP value */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
42025e36332573177ecd7c12730e18a4390f994f05Ben Hutchings	return LLC_PDU_IS_CMD(pdu) &&			/* command PDU */
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       LLC_PDU_TYPE_IS_U(pdu) &&		/* U type PDU */
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       !pdu->dsap ? 0 : 1;			/* NULL DSAP */
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int llc_station_ac_send_xid_r(struct sk_buff *skb)
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 mac_da[ETH_ALEN], dsap;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 1;
52f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park	struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U,
53f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park					       sizeof(struct llc_xid_info));
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!nskb)
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_decode_sa(skb, mac_da);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_decode_ssap(skb, &dsap);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127);
62a5a04819c5740cb1aa217af2cc8f5ef26f33d744Joonwoo Park	rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
63249ff1c6d35fd32ca945967c3f0b948210a96baaArnaldo Carvalho de Melo	if (unlikely(rc))
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free;
655ecf9eea2660c4fe894fabd3c3d0b64860fb0160Ben Hutchings	dev_queue_xmit(nskb);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfree:
6991d27a8650d5359a7a320daeb35b88cdea15e3a8Sorin Dumitru	kfree_skb(nskb);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int llc_station_ac_send_test_r(struct sk_buff *skb)
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 mac_da[ETH_ALEN], dsap;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 1;
77f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park	u32 data_size;
78f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park	struct sk_buff *nskb;
79f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park
80f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park	/* The test request command is type U (llc_len = 3) */
81f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park	data_size = ntohs(eth_hdr(skb)->h_proto) - 3;
82f83f1768f833cb45bc93429fdc552252a4f55ac3Joonwoo Park	nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!nskb)
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_decode_sa(skb, mac_da);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_decode_ssap(skb, &dsap);
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
90d57b1869b231c56de441db35c647879d51c5d29eYOSHIFUJI Hideaki	llc_pdu_init_as_test_rsp(nskb, skb);
91a5a04819c5740cb1aa217af2cc8f5ef26f33d744Joonwoo Park	rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
92249ff1c6d35fd32ca945967c3f0b948210a96baaArnaldo Carvalho de Melo	if (unlikely(rc))
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free;
945ecf9eea2660c4fe894fabd3c3d0b64860fb0160Ben Hutchings	dev_queue_xmit(nskb);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfree:
9891d27a8650d5359a7a320daeb35b88cdea15e3a8Sorin Dumitru	kfree_skb(nskb);
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	llc_station_rcv - send received pdu to the station state machine
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@skb: received frame.
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Sends data unit to station state machine.
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void llc_station_rcv(struct sk_buff *skb)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11012ebc8b9af7e29ff4dc77ee0e73a6b1de513d659Ben Hutchings	if (llc_stat_ev_rx_null_dsap_xid_c(skb))
11112ebc8b9af7e29ff4dc77ee0e73a6b1de513d659Ben Hutchings		llc_station_ac_send_xid_r(skb);
11212ebc8b9af7e29ff4dc77ee0e73a6b1de513d659Ben Hutchings	else if (llc_stat_ev_rx_null_dsap_test_c(skb))
11312ebc8b9af7e29ff4dc77ee0e73a6b1de513d659Ben Hutchings		llc_station_ac_send_test_r(skb);
11404d191c259e2a2832ea7aef14cb02fe03a71d51fBen Hutchings	kfree_skb(skb);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1176024935f5ff5f1646bce8404416318e5fd4a0c4aBen Hutchingsvoid __init llc_station_init(void)
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
119aadf31de16a7b2878af00a02e6557df84efa784bBen Hutchings	llc_set_station_handler(llc_station_rcv);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
122f4f8720febf0d785a054fc09bde5e3ad09728a58Ben Hutchingsvoid llc_station_exit(void)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	llc_set_station_handler(NULL);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
126