Line data Source code
1 : /*
2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include <pkt/proto_handler.h>
7 : #include <diag/diag_pkt_handler.h>
8 : #include <diag/diag_proto.h>
9 : #include <diag/segment_health_check.h>
10 : #include <oper/health_check.h>
11 : #include <oper/metadata_ip.h>
12 :
13 0 : SegmentHealthCheckPkt::SegmentHealthCheckPkt(HealthCheckInstanceService *svc,
14 0 : DiagTable *diag_table) :
15 0 : DiagEntry(svc->source_ip().to_string(),
16 0 : svc->destination_ip().to_string(), IPPROTO_ICMP, 0, 0, "",
17 0 : ((svc->service()->timeout() * 1000) +
18 0 : (svc->service()->timeout_usecs()/1000)),
19 0 : svc->service()->max_retries(),
20 0 : diag_table), service_(svc), state_(SUCCESS) {
21 0 : delay_msecs_ = GetDelay(svc);
22 0 : delay_timer_ = TimerManager::CreateTimer
23 0 : (*(diag_table->agent()->event_manager())->io_service(),
24 : "SegmentHCDelayTimeoutHandler",
25 : TaskScheduler::GetInstance()->GetTaskId("Agent::Diag"),
26 : PktHandler::DIAG);
27 0 : }
28 :
29 :
30 0 : SegmentHealthCheckPkt::~SegmentHealthCheckPkt() {
31 0 : delay_timer_->Cancel();
32 0 : TimerManager::DeleteTimer(delay_timer_);
33 0 : }
34 :
35 0 : int SegmentHealthCheckPkt::GetDelay(const HealthCheckInstanceService *svc)
36 : const {
37 : /* Convert delay from seconds to milliseconds and store */
38 0 : int msecs = svc->service()->delay() * 1000;
39 : /* Convert delay_usecs from micro-seconds to milliseconds and update */
40 0 : msecs += svc->service()->delay_usecs() / 1000;
41 0 : return msecs;
42 : }
43 :
44 0 : int SegmentHealthCheckPkt::GetTimeout(const HealthCheckInstanceService *svc)
45 : const {
46 : /* Convert timeout from seconds to milliseconds and store */
47 0 : int msecs = svc->service()->timeout() * 1000;
48 : /* Convert timeout_usecs from micro-seconds to milliseconds and update */
49 0 : msecs += svc->service()->timeout_usecs() / 1000;
50 0 : return msecs;
51 : }
52 :
53 0 : void SegmentHealthCheckPkt::Retry() {
54 0 : delay_timer_->Cancel();
55 0 : delay_timer_->Start(delay_msecs_,
56 : boost::bind(&SegmentHealthCheckPkt::RetryHandler, this));
57 0 : }
58 :
59 0 : bool SegmentHealthCheckPkt::RetryHandler() {
60 0 : SendRequest();
61 0 : RestartTimer();
62 : /* return false to suppress auto-restart of timer */
63 0 : return false;
64 : }
65 :
66 0 : void SegmentHealthCheckPkt::FillDiagHeader(AgentDiagPktData *data) const {
67 0 : data->op_ = htonl(AgentDiagPktData::DIAG_REQUEST);
68 0 : data->key_ = htons(key_);
69 0 : data->seq_no_ = htonl(seq_no_);
70 0 : strcpy(data->data_, DiagTable::kDiagData.c_str());
71 0 : }
72 :
73 0 : void SegmentHealthCheckPkt::SendRequest() {
74 0 : Agent *agent = diag_table_->agent();
75 :
76 : //Allocate buffer to hold packet
77 : boost::shared_ptr<PktInfo> pkt_info(new PktInfo(agent, kBufferSize,
78 0 : PktHandler::DIAG, 0));
79 0 : uint8_t *msg = pkt_info->packet_buffer()->data();
80 0 : memset(msg, 0, kBufferSize);
81 :
82 : DiagPktHandler *pkt_handler =
83 : new DiagPktHandler(agent, pkt_info,
84 0 : *(agent->event_manager())->io_service());
85 0 : uint16_t len = sizeof(AgentDiagPktData);
86 0 : uint8_t *data = NULL;
87 0 : if (sip_.is_v4()) {
88 : //Update pointers to ethernet header, ip header and l4 header
89 0 : pkt_info->UpdateHeaderPtr();
90 0 : switch (proto_) {
91 0 : case IPPROTO_ICMP:
92 0 : len += 8;
93 0 : data = (uint8_t *)pkt_handler->pkt_info()->transp.icmp + 8;
94 0 : pkt_handler->pkt_info()->transp.icmp->icmp_type = ICMP_ECHO;
95 0 : pkt_handler->pkt_info()->transp.icmp->icmp_code = 0;
96 0 : pkt_handler->pkt_info()->transp.icmp->icmp_cksum = 0xffff;
97 0 : break;
98 0 : default:
99 0 : assert(0);
100 : }
101 : //Add Diag header as ICMP payload
102 0 : FillDiagHeader((AgentDiagPktData *)data);
103 0 : len += sizeof(struct ip);
104 0 : pkt_handler->IpHdr(len, ntohl(sip_.to_v4().to_ulong()),
105 0 : ntohl(dip_.to_v4().to_ulong()),
106 0 : proto_, DEFAULT_IP_ID, DEFAULT_IP_TTL);
107 0 : len += sizeof(ether_header);
108 0 : InterfaceRef interface = service_->interface();
109 :
110 0 : uint32_t service_mode = (static_cast<VmInterface *>(interface.get()))->service_mode();
111 0 : bool l3_mode = false;
112 0 : MacAddress dest_mac;
113 0 : if (service_mode == VmInterface::SERVICE_MODE_ERROR) {
114 0 : delete pkt_handler;
115 0 : return;
116 : }
117 :
118 0 : if ((service_mode == VmInterface::ROUTED_MODE) ||
119 : (service_mode == VmInterface::ROUTED_NAT_MODE)) {
120 0 : l3_mode = true;
121 0 : dest_mac = (static_cast<VmInterface *>(interface.get()))->vm_mac();
122 : }
123 :
124 0 : if ((static_cast<VmInterface *>(interface.get()))->is_left_si()) {
125 0 : if (l3_mode) {
126 0 : pkt_handler->EthHdr(agent->left_si_mac(), dest_mac,
127 : ETHERTYPE_IP);
128 : } else {
129 0 : pkt_handler->EthHdr(agent->left_si_mac(), agent->right_si_mac(),
130 : ETHERTYPE_IP);
131 : }
132 : } else {
133 0 : if (l3_mode) {
134 0 : pkt_handler->EthHdr(agent->right_si_mac(), dest_mac,
135 : ETHERTYPE_IP);
136 : } else {
137 0 : pkt_handler->EthHdr(agent->right_si_mac(), agent->left_si_mac(),
138 : ETHERTYPE_IP);
139 : }
140 : }
141 : } else {
142 : //TODO: support for IPv6
143 0 : assert(0);
144 : }
145 : //Increment the attempt count
146 0 : seq_no_++;
147 :
148 : //Send request out
149 0 : pkt_handler->pkt_info()->set_len(len);
150 0 : pkt_handler->Send(service_->interface()->id(),
151 0 : service_->interface()->vrf_id(), AgentHdr::TX_SWITCH,
152 : CMD_PARAM_PACKET_CTRL, CMD_PARAM_1_DIAG,
153 : PktHandler::DIAG);
154 0 : delete pkt_handler;
155 0 : diag_table_->diag_proto()->IncrementDiagStats(service_->interface()->id(),
156 : DiagProto::REQUESTS_SENT);
157 0 : return;
158 0 : }
159 :
160 0 : void SegmentHealthCheckPkt::RequestTimedOut(uint32_t seqno) {
161 0 : if (seq_no_ >= GetMaxAttempts()) {
162 0 : Notify(FAILURE);
163 0 : seq_no_ = 0;
164 : }
165 0 : Retry();
166 0 : }
167 :
168 0 : void SegmentHealthCheckPkt::HandleReply(DiagPktHandler *handler) {
169 0 : diag_table_->diag_proto()->IncrementDiagStats(service_->interface()->id(),
170 : DiagProto::REPLIES_RECEIVED);
171 0 : seq_no_ = 0;
172 0 : Notify(SUCCESS);
173 : /* Cancel the timeout timer and start delay timer to resend the request */
174 0 : TimerCancel();
175 0 : Retry();
176 0 : }
177 :
178 0 : void SegmentHealthCheckPkt::StopDelayTimer() {
179 0 : delay_timer_->Cancel();
180 0 : }
181 :
182 0 : void SegmentHealthCheckPkt::Notify(Status status) {
183 0 : if (state_ != status) {
184 0 : state_ = status;
185 0 : std::string data = (state_ == SUCCESS) ? "success" : "failure";
186 0 : service_->OnRead(data);
187 0 : }
188 0 : }
189 :
190 0 : void SegmentHealthCheckPkt::UpdateService(HealthCheckInstanceService *service) {
191 0 : service_ = service;
192 0 : delay_msecs_ = GetDelay(service);
193 0 : timeout_ = GetTimeout(service);
194 0 : max_attempts_ = service->service()->max_retries();
195 0 : }
|