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