blob: 4ab4444e4d3a6a93629f99b5e345efcd01e44db2 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <bcmos_system.h>
31
32#include <sys/socket.h>
33#include <sys/types.h>
34#include <netinet/in.h>
35
36#include <bcmtr_plugin.h>
37#include <bcm_config.h>
38
39/* Last seen host IP and port */
40static struct sockaddr_in peer_sa[BCMTR_MAX_OLTS];
41
42/* This plugin stores both device id and per-device socket handle in
43 * transport's channel_id handle as
44 * (device_id << 24) | socket_handle.
45 * The following functions recover socket_handle and device_id from the channel_id handle
46 */
47
48static inline int _bcmtr_channel_to_socket(bcmtr_plugin_channel ch)
49{
50 return (int)(ch & 0xffffff);
51}
52
53static inline int _bcmtr_channel_to_device(bcmtr_plugin_channel ch)
54{
55 return (int)((ch >> 24) & 0xff);
56}
57
58/** Open communication channel */
59static bcmos_errno bcmtr_udp_open(int device, bcmtr_plugin_cfg *cfg, bcmtr_plugin_channel *ch)
60{
61 struct sockaddr_in sa = {};
62 struct timeval timeout = { .tv_sec = 0, .tv_usec = BCMTR_MSG_TIMEOUT * 1000 };
63 int s;
64
65 s = socket(AF_INET, SOCK_DGRAM, 0);
66 if (s < 0)
67 {
68 BCMOS_TRACE_RETURN(BCM_ERR_COMM_FAIL, "Can't create UDP socket\n");
69 }
70
71 /* Bind local */
72 sa.sin_family = AF_INET;
73 sa.sin_port = htons(cfg->x.udp.my_port);
74 sa.sin_addr.s_addr = INADDR_ANY;
75 if (bind(s, (struct sockaddr*)&sa, sizeof(sa) ) == -1)
76 {
77 close(s);
78 perror("bind");
79 BCMOS_TRACE_RETURN(BCM_ERR_COMM_FAIL, "Can't bind UDP socket to %d.%d.%d.%d:%d\n",
80 (int)(cfg->x.udp.my_ip >> 24), (int)((cfg->x.udp.my_ip >> 16) & 0xff),
81 (int)((cfg->x.udp.my_ip >> 8) & 0xff), (int)(cfg->x.udp.my_ip & 0xff),
82 (int)cfg->x.udp.my_port);
83 }
84 bcmos_printf("%s: socket bound to port %d\n", __FUNCTION__, (int)cfg->x.udp.my_port);
85
86 if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
87 {
88 BCMOS_TRACE_RETURN(BCM_ERR_COMM_FAIL, "Can't set socket option for send timeout\n");
89 }
90
91 /* Connect to remote */
92 if (cfg->x.udp.remote_port)
93 {
94 peer_sa[device].sin_family = AF_INET;
95 peer_sa[device].sin_port = htons(cfg->x.udp.remote_port);
96 peer_sa[device].sin_addr.s_addr = htonl(cfg->x.udp.remote_ip);
97 }
98 *ch = (device << 24) | s;
99
100 return BCM_ERR_OK;
101}
102
103/* print error message with throttling: not oftener than ones per second */
104static void _bcmtr_udp_print_err(bcmos_errno err, const char *err_msg)
105{
106 static int nerrors;
107 static uint32_t last_timestamp;
108
109 ++nerrors;
110
111 if (!last_timestamp || bcmos_timestamp() - last_timestamp > 1000000)
112 {
113 BCMOS_TRACE_ERR("Error in low-level UDP transport. %s-%s. Errors since last report: %d\n",
114 err_msg, bcmos_strerror(err), nerrors);
115 last_timestamp = bcmos_timestamp();
116 nerrors = 0;
117 }
118}
119
120/** Close communication channel */
121static bcmos_errno bcmtr_udp_close(bcmtr_plugin_channel ch)
122{
123 close(_bcmtr_channel_to_socket(ch));
124 return BCM_ERR_OK;
125}
126
127/** Send data */
128static bcmos_errno bcmtr_udp_send(bcmtr_plugin_channel ch, bcmolt_subchannel subch, bcmolt_buf *buf,
129 bcmtr_send_flags flags)
130{
131 int sock = _bcmtr_channel_to_socket(ch);
132 int device = _bcmtr_channel_to_device(ch);
133 int buflen = bcmolt_buf_get_used(buf);
134 struct iovec iov = { .iov_base = buf->start, .iov_len = buflen };
135 struct msghdr msg = {
136 .msg_iov = &iov, .msg_iovlen = 1, .msg_flags = 0,
137 .msg_name = (void *)&peer_sa[device], .msg_namelen = sizeof(peer_sa[0])
138 };
139 int len;
140
141 len = sendmsg(sock, &msg, 0);
142 if (len < buflen)
143 {
144 bcmos_printf("%s: sock_sendmsg(%u) --> %d. errno=%d\n", __FUNCTION__, buflen, len, errno);
145 return BCM_ERR_COMM_FAIL;
146 }
147
148 return BCM_ERR_OK;
149}
150
151/** Receive data */
152static bcmos_errno bcmtr_udp_recv(bcmtr_plugin_channel ch, bcmolt_subchannel *subch, bcmolt_buf *buf)
153{
154 int sock = _bcmtr_channel_to_socket(ch);
155 int device = _bcmtr_channel_to_device(ch);
156 struct iovec iov;
157 struct msghdr msg = {
158 .msg_iov = &iov, .msg_iovlen = 1,
159 .msg_name = (void *)&peer_sa[device], .msg_namelen = sizeof(peer_sa[0])
160 };
161 int len;
162 fd_set read_fds;
163 struct timeval tv;
164 bcmos_errno err;
165 int rc;
166
167 FD_ZERO(&read_fds);
168 FD_SET(sock, &read_fds);
169 tv.tv_sec = 0;
170 tv.tv_usec = BCMTR_MSG_TIMEOUT * 1000;
171
172 rc = select(sock + 1, &read_fds, NULL, NULL, &tv);
173 if (rc < 0)
174 {
175 perror("select");
176 return BCM_ERR_COMM_FAIL;
177 }
178 if (!rc || !FD_ISSET(sock, &read_fds))
179 {
180 return BCM_ERR_TIMEOUT;
181 }
182
183 err = bcmolt_buf_alloc(buf, BCMTR_MAX_MTU_SIZE, BCMOLT_BUF_ENDIAN_FIXED);
184 if (err)
185 return BCM_ERR_NOMEM;
186
187 iov.iov_base = buf->start;
188 iov.iov_len = BCMTR_MAX_MTU_SIZE;
189 len = recvmsg(sock, &msg, 0);
190 if (len <= 0)
191 {
192 _bcmtr_udp_print_err(BCM_ERR_COMM_FAIL, "recvmsg() failed\n");
193 bcmolt_buf_free(buf);
194 return BCM_ERR_COMM_FAIL;
195 }
196 buf->len = len;
197 *subch = ntohs(peer_sa[device].sin_port);
198
199 return BCM_ERR_OK;
200}
201
202/** Initialize plugin callbacks
203 * \param[in,out] driver Transport plugin driver structure
204 * \return error code
205 */
206bcmos_errno bcmtr_plugin_init(bcmtr_plugin_cfg *cfg, bcmtr_driver *driver)
207{
208 if (cfg->type != BCMTR_TYPE_UDP)
209 {
210 BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Invalid plugin type. Expected UDP\n");
211 }
212
213 driver->open = bcmtr_udp_open;
214 driver->close = bcmtr_udp_close;
215 driver->recv = bcmtr_udp_recv;
216 driver->send = bcmtr_udp_send;
217
218 return BCM_ERR_OK;
219}
220