blob: b0f84d1fb57ac646b8d69e1605d4a8e0087e919e [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>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/socket.h>
12#include <memory>
Austin Schuh387b7de2020-03-15 14:28:07 -070013#include <thread>
Austin Schuhe84c3ed2019-12-14 15:29:48 -080014
15#include "aos/network/sctp_lib.h"
16#include "aos/unique_malloc_ptr.h"
17#include "glog/logging.h"
18
19namespace aos {
20namespace message_bridge {
21
22SctpServer::SctpServer(std::string_view local_host, int local_port)
Austin Schuh387b7de2020-03-15 14:28:07 -070023 : sockaddr_local_(ResolveSocket(local_host, local_port)) {
24 while (true) {
25 fd_ = socket(sockaddr_local_.ss_family, SOCK_SEQPACKET, IPPROTO_SCTP);
26 LOG(INFO) << "socket(" << Family(sockaddr_local_)
27 << ", SOCK_SEQPACKET, IPPROTOSCTP) = " << fd_;
28 PCHECK(fd_ != -1);
Austin Schuhe84c3ed2019-12-14 15:29:48 -080029
Austin Schuh387b7de2020-03-15 14:28:07 -070030 {
31 struct sctp_event_subscribe subscribe;
32 memset(&subscribe, 0, sizeof(subscribe));
Austin Schuh387b7de2020-03-15 14:28:07 -070033 subscribe.sctp_association_event = 1;
34 subscribe.sctp_send_failure_event = 1;
35 subscribe.sctp_partial_delivery_event = 1;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080036
Austin Schuh387b7de2020-03-15 14:28:07 -070037 PCHECK(setsockopt(fd_, SOL_SCTP, SCTP_EVENTS, (char *)&subscribe,
38 sizeof(subscribe)) == 0);
39 }
40 {
41 // Enable recvinfo when a packet arrives.
42 int on = 1;
43 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on,
44 sizeof(int)) == 0);
45 }
46 {
47 // Allow one packet on the wire to have multiple source packets.
48 int full_interleaving = 2;
49 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE,
50 &full_interleaving, sizeof(full_interleaving)) == 0);
51 }
52 {
53 // Turn off the NAGLE algorithm.
54 int on = 1;
55 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(int)) ==
56 0);
57 }
58
Austin Schuhf6ed4522020-12-13 16:40:38 -080059 {
60 int on = 1;
61 PCHECK(setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) == 0);
62 }
63
Austin Schuh387b7de2020-03-15 14:28:07 -070064 // And go!
65 if (bind(fd_, (struct sockaddr *)&sockaddr_local_,
66 sockaddr_local_.ss_family == AF_INET6
67 ? sizeof(struct sockaddr_in6)
68 : sizeof(struct sockaddr_in)) != 0) {
69 PLOG(ERROR) << "Failed to bind, retrying";
70 close(fd_);
71 std::this_thread::sleep_for(std::chrono::seconds(5));
72 continue;
73 }
74 LOG(INFO) << "bind(" << fd_ << ", " << Address(sockaddr_local_) << ")";
75
76 PCHECK(listen(fd_, 100) == 0);
77
78 SetMaxSize(1000);
79 break;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080080 }
Austin Schuhe84c3ed2019-12-14 15:29:48 -080081}
82
83aos::unique_c_ptr<Message> SctpServer::Read() {
84 return ReadSctpMessage(fd_, max_size_);
85}
86
Austin Schuh4889b182020-11-18 19:11:56 -080087bool SctpServer::Abort(sctp_assoc_t snd_assoc_id) {
88 // Use the assoc_id for the destination instead of the msg_name.
89 struct msghdr outmsg;
90 outmsg.msg_namelen = 0;
91
92 outmsg.msg_iovlen = 0;
93
94 // Build up the sndinfo message.
95 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
96 outmsg.msg_control = outcmsg;
97 outmsg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
98 outmsg.msg_flags = 0;
99
100 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&outmsg);
101 cmsg->cmsg_level = IPPROTO_SCTP;
102 cmsg->cmsg_type = SCTP_SNDRCV;
103 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
104
105 struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
106 memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
107 sinfo->sinfo_ppid = ++ppid_;
108 sinfo->sinfo_stream = 0;
109 sinfo->sinfo_flags = SCTP_ABORT;
110 sinfo->sinfo_assoc_id = snd_assoc_id;
111
112 // And send.
113 const ssize_t size = sendmsg(fd_, &outmsg, MSG_NOSIGNAL | MSG_DONTWAIT);
114 if (size == -1) {
115 if (errno == EPIPE || errno == EAGAIN || errno == ESHUTDOWN) {
116 return false;
117 }
118 return false;
119 } else {
120 CHECK_EQ(0, size);
121 return true;
122 }
123}
124
Austin Schuh83afb7a2020-03-15 23:09:22 -0700125bool SctpServer::Send(std::string_view data, sctp_assoc_t snd_assoc_id,
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800126 int stream, int timetolive) {
127 struct iovec iov;
128 iov.iov_base = const_cast<char *>(data.data());
129 iov.iov_len = data.size();
130
131 // Use the assoc_id for the destination instead of the msg_name.
132 struct msghdr outmsg;
133 outmsg.msg_namelen = 0;
134
135 // Data to send.
136 outmsg.msg_iov = &iov;
137 outmsg.msg_iovlen = 1;
138
139 // Build up the sndinfo message.
140 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
141 outmsg.msg_control = outcmsg;
142 outmsg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
143 outmsg.msg_flags = 0;
144
145 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&outmsg);
146 cmsg->cmsg_level = IPPROTO_SCTP;
147 cmsg->cmsg_type = SCTP_SNDRCV;
148 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
149
150 struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
151 memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
152 sinfo->sinfo_ppid = ++ppid_;
153 sinfo->sinfo_stream = stream;
154 sinfo->sinfo_flags = 0;
155 sinfo->sinfo_assoc_id = snd_assoc_id;
156 sinfo->sinfo_timetolive = timetolive;
157
158 // And send.
159 const ssize_t size = sendmsg(fd_, &outmsg, MSG_NOSIGNAL | MSG_DONTWAIT);
160 if (size == -1) {
Austin Schuh40629422020-03-29 23:12:12 -0700161 if (errno == EPIPE || errno == EAGAIN || errno == ESHUTDOWN) {
Austin Schuh83afb7a2020-03-15 23:09:22 -0700162 return false;
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800163 }
Austin Schuh83afb7a2020-03-15 23:09:22 -0700164 PCHECK(size == static_cast<ssize_t>(data.size()));
165 return false;
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800166 } else {
167 CHECK_EQ(static_cast<ssize_t>(data.size()), size);
Austin Schuh83afb7a2020-03-15 23:09:22 -0700168 return true;
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800169 }
170}
171
172void SctpServer::SetPriorityScheduler(sctp_assoc_t assoc_id) {
173 struct sctp_assoc_value scheduler;
174 memset(&scheduler, 0, sizeof(scheduler));
175 scheduler.assoc_id = assoc_id;
176 scheduler.assoc_value = SCTP_SS_PRIO;
177 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER, &scheduler,
178 sizeof(scheduler)) != 0) {
179 PLOG(WARNING) << "Failed to set scheduler";
180 }
181}
182
183void SctpServer::SetStreamPriority(sctp_assoc_t assoc_id, int stream_id,
184 uint16_t priority) {
185 struct sctp_stream_value sctp_priority;
186 memset(&sctp_priority, 0, sizeof(sctp_priority));
187 sctp_priority.assoc_id = assoc_id;
188 sctp_priority.stream_id = stream_id;
189 sctp_priority.stream_value = priority;
190 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER_VALUE,
191 &sctp_priority, sizeof(sctp_priority)) != 0) {
192 PLOG(WARNING) << "Failed to set scheduler";
193 }
194}
195
196} // namespace message_bridge
197} // namespace aos