blob: 9e77a84bdd5666a29e4adb0657612e0a3c5ce2d9 [file] [log] [blame]
Austin Schuhe84c3ed2019-12-14 15:29:48 -08001#include "aos/network/sctp_client.h"
2
3#include <arpa/inet.h>
4#include <net/if.h>
5#include <netinet/sctp.h>
Austin Schuhe84c3ed2019-12-14 15:29:48 -08006#include <sys/socket.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07007
8#include <cstdlib>
9#include <cstring>
Austin Schuhe84c3ed2019-12-14 15:29:48 -080010#include <string_view>
11
12#include "aos/network/sctp_lib.h"
13#include "aos/unique_malloc_ptr.h"
14#include "glog/logging.h"
15
16namespace aos {
17namespace message_bridge {
18
19SctpClient::SctpClient(std::string_view remote_host, int remote_port,
20 int streams, std::string_view local_host, int local_port)
21 : sockaddr_remote_(ResolveSocket(remote_host, remote_port)),
22 sockaddr_local_(ResolveSocket(local_host, local_port)),
23 fd_(socket(sockaddr_local_.ss_family, SOCK_SEQPACKET, IPPROTO_SCTP)) {
24 LOG(INFO) << "socket(" << Family(sockaddr_local_)
25 << ", SOCK_SEQPACKET, IPPROTOSCTP) = " << fd_;
26 PCHECK(fd_ != -1);
27
28 {
Austin Schuh80b055b2021-03-31 21:06:24 -070029 // Per https://tools.ietf.org/html/rfc6458
30 // Setting this to !0 allows event notifications to be interleaved
31 // with data if enabled, and would have to be handled in the code.
32 // Enabling interleaving would only matter during congestion, which
33 // typically only happens during application startup.
34 int interleaving = 0;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080035 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE,
Austin Schuh80b055b2021-03-31 21:06:24 -070036 &interleaving, sizeof(interleaving)) == 0);
Austin Schuhe84c3ed2019-12-14 15:29:48 -080037 }
38
39 {
40 struct sctp_initmsg initmsg;
41 memset(&initmsg, 0, sizeof(struct sctp_initmsg));
42 initmsg.sinit_num_ostreams = streams;
43 initmsg.sinit_max_instreams = streams;
44 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
45 sizeof(struct sctp_initmsg)) == 0);
46 }
47
48 {
49 int on = 1;
50 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int)) ==
51 0);
52 }
53 {
54 // Servers send promptly. Clients don't.
55 // TODO(austin): Revisit this assumption when we have time sync.
56 int on = 0;
57 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(int)) == 0);
58 }
59
60 {
61 // TODO(austin): This is the old style registration... But, the sctp
62 // stack out in the wild for linux is old and primitive.
63 struct sctp_event_subscribe subscribe;
64 memset(&subscribe, 0, sizeof(subscribe));
Austin Schuhe84c3ed2019-12-14 15:29:48 -080065 subscribe.sctp_association_event = 1;
Austin Schuhf7777002020-09-01 18:41:28 -070066 subscribe.sctp_stream_change_event = 1;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080067 PCHECK(setsockopt(fd_, SOL_SCTP, SCTP_EVENTS, (char *)&subscribe,
68 sizeof(subscribe)) == 0);
69 }
70
71 PCHECK(bind(fd_, (struct sockaddr *)&sockaddr_local_,
72 sockaddr_local_.ss_family == AF_INET6
73 ? sizeof(struct sockaddr_in6)
74 : sizeof(struct sockaddr_in)) == 0);
75 VLOG(1) << "bind(" << fd_ << ", " << Address(sockaddr_local_) << ")";
76}
77
78aos::unique_c_ptr<Message> SctpClient::Read() {
79 return ReadSctpMessage(fd_, max_size_);
80}
81
82bool SctpClient::Send(int stream, std::string_view data, int time_to_live) {
83 struct iovec iov;
84 iov.iov_base = const_cast<char *>(data.data());
85 iov.iov_len = data.size();
86
87 struct msghdr outmsg;
88 // Target to send to.
89 outmsg.msg_name = &sockaddr_remote_;
90 outmsg.msg_namelen = sizeof(struct sockaddr_storage);
91 VLOG(1) << "Sending to " << Address(sockaddr_remote_);
92
93 // Data to send.
94 outmsg.msg_iov = &iov;
95 outmsg.msg_iovlen = 1;
96
97 // Build up the sndinfo message.
98 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
99 outmsg.msg_control = outcmsg;
100 outmsg.msg_controllen = sizeof(outcmsg);
101 outmsg.msg_flags = 0;
102
103 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&outmsg);
104 cmsg->cmsg_level = IPPROTO_SCTP;
105 cmsg->cmsg_type = SCTP_SNDRCV;
106 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
107
108 outmsg.msg_controllen = cmsg->cmsg_len;
109 struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
110 memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
111 sinfo->sinfo_ppid = rand();
112 sinfo->sinfo_stream = stream;
113 sinfo->sinfo_context = 19;
114 sinfo->sinfo_flags = 0;
115 sinfo->sinfo_timetolive = time_to_live;
116
117 // And send.
118 const ssize_t size = sendmsg(fd_, &outmsg, MSG_NOSIGNAL | MSG_DONTWAIT);
119 if (size == -1) {
Austin Schuh871da992020-03-28 23:03:02 -0700120 if (errno != EPIPE && errno != EAGAIN && errno != ESHUTDOWN) {
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800121 PCHECK(size == static_cast<ssize_t>(data.size()));
122 } else {
123 return false;
124 }
125 } else {
126 CHECK_EQ(static_cast<ssize_t>(data.size()), size);
127 }
128
129 VLOG(1) << "Sent " << data.size();
130 return true;
131}
132
133void SctpClient::LogSctpStatus(sctp_assoc_t assoc_id) {
134 message_bridge::LogSctpStatus(fd(), assoc_id);
135}
136
137void SctpClient::SetPriorityScheduler(sctp_assoc_t assoc_id) {
Jim Ostrowski5d5a44f2020-06-24 19:10:15 -0700138 struct sctp_assoc_value scheduler;
139 memset(&scheduler, 0, sizeof(scheduler));
140 scheduler.assoc_id = assoc_id;
141 scheduler.assoc_value = SCTP_SS_PRIO;
142 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER, &scheduler,
143 sizeof(scheduler)) != 0) {
144 PLOG(WARNING) << "Failed to set scheduler";
145 }
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800146}
147
148} // namespace message_bridge
149} // namespace aos