blob: 47a57198f132a6c11ed347821df4166ab140303e [file] [log] [blame]
Austin Schuhe84c3ed2019-12-14 15:29:48 -08001#include "aos/network/sctp_server.h"
2
3#include <arpa/inet.h>
4#include <net/if.h>
5#include <netdb.h>
6#include <netinet/in.h>
7#include <netinet/sctp.h>
Austin Schuhe84c3ed2019-12-14 15:29:48 -08008#include <sys/socket.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07009
10#include <cstdio>
11#include <cstdlib>
12#include <cstring>
Austin Schuhe84c3ed2019-12-14 15:29:48 -080013#include <memory>
Austin Schuh387b7de2020-03-15 14:28:07 -070014#include <thread>
Austin Schuhe84c3ed2019-12-14 15:29:48 -080015
16#include "aos/network/sctp_lib.h"
17#include "aos/unique_malloc_ptr.h"
18#include "glog/logging.h"
19
20namespace aos {
21namespace message_bridge {
22
23SctpServer::SctpServer(std::string_view local_host, int local_port)
Austin Schuh387b7de2020-03-15 14:28:07 -070024 : sockaddr_local_(ResolveSocket(local_host, local_port)) {
25 while (true) {
26 fd_ = socket(sockaddr_local_.ss_family, SOCK_SEQPACKET, IPPROTO_SCTP);
27 LOG(INFO) << "socket(" << Family(sockaddr_local_)
28 << ", SOCK_SEQPACKET, IPPROTOSCTP) = " << fd_;
29 PCHECK(fd_ != -1);
Austin Schuhe84c3ed2019-12-14 15:29:48 -080030
Austin Schuh387b7de2020-03-15 14:28:07 -070031 {
32 struct sctp_event_subscribe subscribe;
33 memset(&subscribe, 0, sizeof(subscribe));
Austin Schuh387b7de2020-03-15 14:28:07 -070034 subscribe.sctp_association_event = 1;
35 subscribe.sctp_send_failure_event = 1;
36 subscribe.sctp_partial_delivery_event = 1;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080037
Austin Schuh387b7de2020-03-15 14:28:07 -070038 PCHECK(setsockopt(fd_, SOL_SCTP, SCTP_EVENTS, (char *)&subscribe,
39 sizeof(subscribe)) == 0);
40 }
41 {
42 // Enable recvinfo when a packet arrives.
43 int on = 1;
44 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on,
45 sizeof(int)) == 0);
46 }
47 {
Austin Schuh80b055b2021-03-31 21:06:24 -070048 // Per https://tools.ietf.org/html/rfc6458
49 // Setting this to !0 allows event notifications to be interleaved
50 // with data if enabled, and would have to be handled in the code.
51 // Enabling interleaving would only matter during congestion, which
52 // typically only happens during application startup.
53 int interleaving = 0;
Austin Schuh387b7de2020-03-15 14:28:07 -070054 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE,
Austin Schuh80b055b2021-03-31 21:06:24 -070055 &interleaving, sizeof(interleaving)) == 0);
Austin Schuh387b7de2020-03-15 14:28:07 -070056 }
57 {
58 // Turn off the NAGLE algorithm.
59 int on = 1;
60 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(int)) ==
61 0);
62 }
63
Austin Schuhf6ed4522020-12-13 16:40:38 -080064 {
65 int on = 1;
66 PCHECK(setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) == 0);
67 }
68
Austin Schuh387b7de2020-03-15 14:28:07 -070069 // And go!
70 if (bind(fd_, (struct sockaddr *)&sockaddr_local_,
71 sockaddr_local_.ss_family == AF_INET6
72 ? sizeof(struct sockaddr_in6)
73 : sizeof(struct sockaddr_in)) != 0) {
74 PLOG(ERROR) << "Failed to bind, retrying";
75 close(fd_);
76 std::this_thread::sleep_for(std::chrono::seconds(5));
77 continue;
78 }
79 LOG(INFO) << "bind(" << fd_ << ", " << Address(sockaddr_local_) << ")";
80
81 PCHECK(listen(fd_, 100) == 0);
82
83 SetMaxSize(1000);
84 break;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080085 }
Austin Schuhe84c3ed2019-12-14 15:29:48 -080086}
87
88aos::unique_c_ptr<Message> SctpServer::Read() {
89 return ReadSctpMessage(fd_, max_size_);
90}
91
Austin Schuh4889b182020-11-18 19:11:56 -080092bool SctpServer::Abort(sctp_assoc_t snd_assoc_id) {
93 // Use the assoc_id for the destination instead of the msg_name.
94 struct msghdr outmsg;
95 outmsg.msg_namelen = 0;
96
97 outmsg.msg_iovlen = 0;
98
99 // Build up the sndinfo message.
100 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
101 outmsg.msg_control = outcmsg;
102 outmsg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
103 outmsg.msg_flags = 0;
104
105 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&outmsg);
106 cmsg->cmsg_level = IPPROTO_SCTP;
107 cmsg->cmsg_type = SCTP_SNDRCV;
108 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
109
110 struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
111 memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
112 sinfo->sinfo_ppid = ++ppid_;
113 sinfo->sinfo_stream = 0;
114 sinfo->sinfo_flags = SCTP_ABORT;
115 sinfo->sinfo_assoc_id = snd_assoc_id;
116
117 // And send.
118 const ssize_t size = sendmsg(fd_, &outmsg, MSG_NOSIGNAL | MSG_DONTWAIT);
119 if (size == -1) {
120 if (errno == EPIPE || errno == EAGAIN || errno == ESHUTDOWN) {
121 return false;
122 }
123 return false;
124 } else {
125 CHECK_EQ(0, size);
126 return true;
127 }
128}
129
Austin Schuh83afb7a2020-03-15 23:09:22 -0700130bool SctpServer::Send(std::string_view data, sctp_assoc_t snd_assoc_id,
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800131 int stream, int timetolive) {
132 struct iovec iov;
133 iov.iov_base = const_cast<char *>(data.data());
134 iov.iov_len = data.size();
135
136 // Use the assoc_id for the destination instead of the msg_name.
137 struct msghdr outmsg;
138 outmsg.msg_namelen = 0;
139
140 // Data to send.
141 outmsg.msg_iov = &iov;
142 outmsg.msg_iovlen = 1;
143
144 // Build up the sndinfo message.
145 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
146 outmsg.msg_control = outcmsg;
147 outmsg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
148 outmsg.msg_flags = 0;
149
150 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&outmsg);
151 cmsg->cmsg_level = IPPROTO_SCTP;
152 cmsg->cmsg_type = SCTP_SNDRCV;
153 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
154
155 struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
156 memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
157 sinfo->sinfo_ppid = ++ppid_;
158 sinfo->sinfo_stream = stream;
159 sinfo->sinfo_flags = 0;
160 sinfo->sinfo_assoc_id = snd_assoc_id;
161 sinfo->sinfo_timetolive = timetolive;
162
163 // And send.
164 const ssize_t size = sendmsg(fd_, &outmsg, MSG_NOSIGNAL | MSG_DONTWAIT);
165 if (size == -1) {
Austin Schuh40629422020-03-29 23:12:12 -0700166 if (errno == EPIPE || errno == EAGAIN || errno == ESHUTDOWN) {
Austin Schuh83afb7a2020-03-15 23:09:22 -0700167 return false;
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800168 }
Austin Schuh83afb7a2020-03-15 23:09:22 -0700169 PCHECK(size == static_cast<ssize_t>(data.size()));
170 return false;
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800171 } else {
172 CHECK_EQ(static_cast<ssize_t>(data.size()), size);
Austin Schuh83afb7a2020-03-15 23:09:22 -0700173 return true;
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800174 }
175}
176
177void SctpServer::SetPriorityScheduler(sctp_assoc_t assoc_id) {
178 struct sctp_assoc_value scheduler;
179 memset(&scheduler, 0, sizeof(scheduler));
180 scheduler.assoc_id = assoc_id;
181 scheduler.assoc_value = SCTP_SS_PRIO;
182 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER, &scheduler,
183 sizeof(scheduler)) != 0) {
184 PLOG(WARNING) << "Failed to set scheduler";
185 }
186}
187
188void SctpServer::SetStreamPriority(sctp_assoc_t assoc_id, int stream_id,
189 uint16_t priority) {
190 struct sctp_stream_value sctp_priority;
191 memset(&sctp_priority, 0, sizeof(sctp_priority));
192 sctp_priority.assoc_id = assoc_id;
193 sctp_priority.stream_id = stream_id;
194 sctp_priority.stream_value = priority;
195 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER_VALUE,
196 &sctp_priority, sizeof(sctp_priority)) != 0) {
197 PLOG(WARNING) << "Failed to set scheduler";
198 }
199}
200
201} // namespace message_bridge
202} // namespace aos