1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * llc_station.c - station component of LLC 4 * 5 * Copyright (c) 1997 by Procom Technology, Inc. 6 * 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> 7 */ 8 #include <linux/init.h> 9 #include <linux/module.h> 10 #include <linux/slab.h> 11 #include <net/llc.h> 12 #include <net/llc_sap.h> 13 #include <net/llc_conn.h> 14 #include <net/llc_c_ac.h> 15 #include <net/llc_s_ac.h> 16 #include <net/llc_c_ev.h> 17 #include <net/llc_c_st.h> 18 #include <net/llc_s_ev.h> 19 #include <net/llc_s_st.h> 20 #include <net/llc_pdu.h> 21 22 static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) 23 { 24 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 25 26 return LLC_PDU_IS_CMD(pdu) && /* command PDU */ 27 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 28 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && 29 !pdu->dsap; /* NULL DSAP value */ 30 } 31 32 static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) 33 { 34 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 35 36 return LLC_PDU_IS_CMD(pdu) && /* command PDU */ 37 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 38 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && 39 !pdu->dsap; /* NULL DSAP */ 40 } 41 42 static int llc_station_ac_send_xid_r(struct sk_buff *skb) 43 { 44 u8 mac_da[ETH_ALEN], dsap; 45 int rc = 1; 46 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 47 sizeof(struct llc_xid_info)); 48 49 if (!nskb) 50 goto out; 51 llc_pdu_decode_sa(skb, mac_da); 52 llc_pdu_decode_ssap(skb, &dsap); 53 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 54 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 55 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 56 if (unlikely(rc)) 57 goto free; 58 dev_queue_xmit(nskb); 59 out: 60 return rc; 61 free: 62 kfree_skb(nskb); 63 goto out; 64 } 65 66 static int llc_station_ac_send_test_r(struct sk_buff *skb) 67 { 68 u8 mac_da[ETH_ALEN], dsap; 69 int rc = 1; 70 u32 data_size; 71 struct sk_buff *nskb; 72 73 if (skb->mac_len < ETH_HLEN) 74 goto out; 75 76 /* The test request command is type U (llc_len = 3) */ 77 data_size = ntohs(eth_hdr(skb)->h_proto) - 3; 78 nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size); 79 80 if (!nskb) 81 goto out; 82 llc_pdu_decode_sa(skb, mac_da); 83 llc_pdu_decode_ssap(skb, &dsap); 84 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 85 llc_pdu_init_as_test_rsp(nskb, skb); 86 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 87 if (unlikely(rc)) 88 goto free; 89 dev_queue_xmit(nskb); 90 out: 91 return rc; 92 free: 93 kfree_skb(nskb); 94 goto out; 95 } 96 97 /** 98 * llc_station_rcv - send received pdu to the station state machine 99 * @skb: received frame. 100 * 101 * Sends data unit to station state machine. 102 */ 103 static void llc_station_rcv(struct sk_buff *skb) 104 { 105 if (llc_stat_ev_rx_null_dsap_xid_c(skb)) 106 llc_station_ac_send_xid_r(skb); 107 else if (llc_stat_ev_rx_null_dsap_test_c(skb)) 108 llc_station_ac_send_test_r(skb); 109 kfree_skb(skb); 110 } 111 112 void __init llc_station_init(void) 113 { 114 llc_set_station_handler(llc_station_rcv); 115 } 116 117 void llc_station_exit(void) 118 { 119 llc_set_station_handler(NULL); 120 } 121