blob: 70d5b286796ce0742801d611e4009c4de97169d8 [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>
13
14#include "aos/network/sctp_lib.h"
15#include "aos/unique_malloc_ptr.h"
16#include "glog/logging.h"
17
18namespace aos {
19namespace message_bridge {
20
21SctpServer::SctpServer(std::string_view local_host, int local_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 {
29 struct sctp_event_subscribe subscribe;
30 memset(&subscribe, 0, sizeof(subscribe));
31 subscribe.sctp_data_io_event = 1;
32 subscribe.sctp_association_event = 1;
33 subscribe.sctp_send_failure_event = 1;
34 subscribe.sctp_partial_delivery_event = 1;
35
36 PCHECK(setsockopt(fd_, SOL_SCTP, SCTP_EVENTS, (char *)&subscribe,
37 sizeof(subscribe)) == 0);
38 }
39 {
40 // Enable recvinfo when a packet arrives.
41 int on = 1;
42 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int)) ==
43 0);
44 }
45 {
46 // Allow one packet on the wire to have multiple source packets.
47 int full_interleaving = 2;
48 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE,
49 &full_interleaving, sizeof(full_interleaving)) == 0);
50 }
51 {
52 // Turn off the NAGLE algorithm.
53 int on = 1;
54 PCHECK(setsockopt(fd_, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(int)) == 0);
55 }
56
57 // And go!
58 PCHECK(bind(fd_, (struct sockaddr *)&sockaddr_local_,
59 sockaddr_local_.ss_family == AF_INET6
60 ? sizeof(struct sockaddr_in6)
61 : sizeof(struct sockaddr_in)) == 0);
62 LOG(INFO) << "bind(" << fd_ << ", " << Address(sockaddr_local_) << ")";
63
64 PCHECK(listen(fd_, 100) == 0);
65
66 PCHECK(setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &max_size_,
67 sizeof(max_size_)) == 0);
68}
69
70aos::unique_c_ptr<Message> SctpServer::Read() {
71 return ReadSctpMessage(fd_, max_size_);
72}
73
74void SctpServer::Send(std::string_view data, sctp_assoc_t snd_assoc_id,
75 int stream, int timetolive) {
76 struct iovec iov;
77 iov.iov_base = const_cast<char *>(data.data());
78 iov.iov_len = data.size();
79
80 // Use the assoc_id for the destination instead of the msg_name.
81 struct msghdr outmsg;
82 outmsg.msg_namelen = 0;
83
84 // Data to send.
85 outmsg.msg_iov = &iov;
86 outmsg.msg_iovlen = 1;
87
88 // Build up the sndinfo message.
89 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
90 outmsg.msg_control = outcmsg;
91 outmsg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
92 outmsg.msg_flags = 0;
93
94 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&outmsg);
95 cmsg->cmsg_level = IPPROTO_SCTP;
96 cmsg->cmsg_type = SCTP_SNDRCV;
97 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
98
99 struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
100 memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
101 sinfo->sinfo_ppid = ++ppid_;
102 sinfo->sinfo_stream = stream;
103 sinfo->sinfo_flags = 0;
104 sinfo->sinfo_assoc_id = snd_assoc_id;
105 sinfo->sinfo_timetolive = timetolive;
106
107 // And send.
108 const ssize_t size = sendmsg(fd_, &outmsg, MSG_NOSIGNAL | MSG_DONTWAIT);
109 if (size == -1) {
110 if (errno != EPIPE) {
111 PCHECK(size == static_cast<ssize_t>(data.size()));
112 }
113 } else {
114 CHECK_EQ(static_cast<ssize_t>(data.size()), size);
115 }
116}
117
118void SctpServer::SetPriorityScheduler(sctp_assoc_t assoc_id) {
119 struct sctp_assoc_value scheduler;
120 memset(&scheduler, 0, sizeof(scheduler));
121 scheduler.assoc_id = assoc_id;
122 scheduler.assoc_value = SCTP_SS_PRIO;
123 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER, &scheduler,
124 sizeof(scheduler)) != 0) {
125 PLOG(WARNING) << "Failed to set scheduler";
126 }
127}
128
129void SctpServer::SetStreamPriority(sctp_assoc_t assoc_id, int stream_id,
130 uint16_t priority) {
131 struct sctp_stream_value sctp_priority;
132 memset(&sctp_priority, 0, sizeof(sctp_priority));
133 sctp_priority.assoc_id = assoc_id;
134 sctp_priority.stream_id = stream_id;
135 sctp_priority.stream_value = priority;
136 if (setsockopt(fd(), IPPROTO_SCTP, SCTP_STREAM_SCHEDULER_VALUE,
137 &sctp_priority, sizeof(sctp_priority)) != 0) {
138 PLOG(WARNING) << "Failed to set scheduler";
139 }
140}
141
142} // namespace message_bridge
143} // namespace aos