Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include "base/util.h"
7 : #include "base/logging.h"
8 : #include "base/connection_info.h"
9 : #include "base/address_util.h"
10 : #include "cmn/agent_cmn.h"
11 : #include "controller/controller_dns.h"
12 : #include "controller/controller_init.h"
13 : #include "xmpp/xmpp_channel.h"
14 : #include "pugixml/pugixml.hpp"
15 : #include "xml/xml_pugi.h"
16 : #include "bind/xmpp_dns_agent.h"
17 :
18 : using process::ConnectionState;
19 : using process::ConnectionType;
20 : using process::ConnectionStatus;
21 :
22 : AgentDnsXmppChannel::DnsMessageHandler AgentDnsXmppChannel::dns_message_handler_cb_;
23 : AgentDnsXmppChannel::DnsXmppEventHandler AgentDnsXmppChannel::dns_xmpp_event_handler_cb_;
24 :
25 2 : AgentDnsXmppChannel::AgentDnsXmppChannel(Agent *agent,
26 2 : std::string xmpp_server, uint8_t xs_idx)
27 4 : : channel_(NULL), xmpp_server_(xmpp_server), xs_idx_(xs_idx),
28 2 : agent_(agent) {
29 2 : }
30 :
31 4 : AgentDnsXmppChannel::~AgentDnsXmppChannel() {
32 2 : if (channel_) {
33 2 : channel_->UnRegisterWriteReady(xmps::DNS);
34 2 : channel_->UnRegisterReceive(xmps::DNS);
35 : }
36 4 : }
37 :
38 2 : void AgentDnsXmppChannel::RegisterXmppChannel(XmppChannel *channel) {
39 2 : if (channel == NULL)
40 0 : return;
41 :
42 2 : channel_ = channel;
43 2 : channel->RegisterReceive(xmps::DNS,
44 : boost::bind(&AgentDnsXmppChannel::ReceiveInternal, this, _1));
45 : }
46 :
47 0 : bool AgentDnsXmppChannel::SendMsg(uint8_t *msg, std::size_t len) {
48 0 : if (!channel_ || channel_->GetPeerState() != xmps::READY)
49 0 : return false;
50 :
51 0 : return channel_->Send((const uint8_t *)msg, len, xmps::DNS,
52 0 : boost::bind(&AgentDnsXmppChannel::WriteReadyCb, this, _1));
53 : }
54 :
55 0 : void AgentDnsXmppChannel::ReceiveMsg(const XmppStanza::XmppMessage *msg) {
56 0 : if (msg && msg->type == XmppStanza::IQ_STANZA) {
57 0 : std::unique_ptr<XmlBase> impl(XmppXmlImplFactory::Instance()->GetXmlImpl());
58 0 : XmlPugi *pugi = reinterpret_cast<XmlPugi *>(impl.get());
59 0 : XmlPugi *msg_pugi = reinterpret_cast<XmlPugi *>(msg->dom.get());
60 0 : pugi->LoadXmlDoc(msg_pugi->doc()); //Verify Xmpp message format
61 : boost::shared_ptr<ControllerXmppData> data(new ControllerXmppData(xmps::DNS,
62 : xmps::UNKNOWN,
63 0 : xs_idx_,
64 0 : std::move(impl),
65 0 : true));
66 0 : agent_->controller()->Enqueue(data);
67 0 : }
68 0 : }
69 :
70 0 : void AgentDnsXmppChannel::ReceiveInternal(const XmppStanza::XmppMessage *msg) {
71 0 : ReceiveMsg(msg);
72 0 : }
73 :
74 0 : void AgentDnsXmppChannel::ReceiveDnsMessage(std::unique_ptr<XmlBase> impl) {
75 0 : XmlPugi *pugi = reinterpret_cast<XmlPugi *>(impl.get());
76 0 : pugi::xml_node node = pugi->FindNode("dns");
77 : DnsAgentXmpp::XmppType xmpp_type;
78 : uint32_t xid;
79 : uint16_t code;
80 0 : std::unique_ptr<DnsUpdateData> xmpp_data(new DnsUpdateData);
81 0 : if (DnsAgentXmpp::DnsAgentXmppDecode(node, xmpp_type, xid,
82 : code, xmpp_data.get())) {
83 0 : if (!dns_message_handler_cb_.empty())
84 0 : dns_message_handler_cb_(xmpp_data.release(), xmpp_type,
85 : NULL, false);
86 : }
87 0 : }
88 :
89 0 : std::string AgentDnsXmppChannel::ToString() const {
90 0 : return channel_->ToString();
91 : }
92 :
93 0 : void AgentDnsXmppChannel::WriteReadyCb(const boost::system::error_code &ec) {
94 0 : }
95 :
96 0 : void AgentDnsXmppChannel::XmppClientChannelEvent(AgentDnsXmppChannel *peer,
97 : xmps::PeerState state) {
98 0 : std::unique_ptr<XmlBase> dummy_dom;
99 : boost::shared_ptr<ControllerXmppData> data(new ControllerXmppData(xmps::DNS,
100 : state,
101 0 : peer->GetXmppServerIdx(),
102 0 : std::move(dummy_dom),
103 0 : false));
104 0 : peer->agent()->controller()->Enqueue(data);
105 0 : }
106 :
107 : /*
108 : * AgentDnsXmppChannel state TimedOut
109 : *
110 : * If there are more than two channels available in config, then try picking
111 : * other channel to replace this timed out channel. And push this channel at the
112 : * end of the channel list so that it is picked only in worst case.
113 : *
114 : * If there are only two channels configured, no action to be taken.
115 : */
116 0 : void AgentDnsXmppChannel::TimedOut() {
117 0 : bool update_list = false;
118 0 : std::vector<string>::iterator iter = agent_->GetDnslist().begin();
119 0 : std::vector<string>::iterator end = agent_->GetDnslist().end();
120 0 : for (; iter != end; iter++) {
121 0 : std::vector<string> server;
122 0 : boost::split(server, *iter, boost::is_any_of(":"));
123 0 : if (GetXmppServer().compare(server[0]) == 0) {
124 : // Add the TIMEDOUT server to the end.
125 0 : if (iter + 1 == end) break;
126 0 : std::rotate(iter, iter + 1, end);
127 0 : update_list = true;
128 0 : break;
129 : }
130 0 : }
131 0 : if (update_list) {
132 0 : agent_->controller()->ReConnectDnsServer();
133 : }
134 0 : }
135 :
136 0 : void AgentDnsXmppChannel::HandleXmppClientChannelEvent(AgentDnsXmppChannel *peer,
137 : xmps::PeerState state) {
138 0 : peer->UpdateConnectionInfo(state);
139 0 : if (state == xmps::READY) {
140 0 : if (!peer->dns_xmpp_event_handler_cb_.empty())
141 0 : peer->dns_xmpp_event_handler_cb_(peer);
142 0 : } else if (state == xmps::TIMEDOUT) {
143 0 : peer->TimedOut();
144 : }
145 0 : }
146 :
147 2 : void AgentDnsXmppChannel::set_dns_message_handler_cb(DnsMessageHandler cb) {
148 2 : dns_message_handler_cb_ = cb;
149 2 : }
150 :
151 2 : void AgentDnsXmppChannel::set_dns_xmpp_event_handler_cb(DnsXmppEventHandler cb){
152 2 : dns_xmpp_event_handler_cb_ = cb;
153 2 : }
154 :
155 2 : void AgentDnsXmppChannel::UpdateConnectionInfo(xmps::PeerState state) {
156 2 : if (agent_->connection_state() == NULL)
157 0 : return;
158 :
159 2 : boost::asio::ip::tcp::endpoint ep;
160 2 : boost::system::error_code ec;
161 2 : std::string last_state_name;
162 2 : ep.address(AddressFromString(agent_->dns_server(xs_idx_), &ec));
163 2 : ep.port(agent_->dns_server_port(xs_idx_));
164 2 : const std::string name = agent_->xmpp_dns_server_prefix() +
165 4 : ep.address().to_string();
166 2 : XmppChannel *xc = GetXmppChannel();
167 2 : if (xc) {
168 2 : last_state_name = xc->LastStateName();
169 : }
170 2 : if (state == xmps::READY) {
171 0 : agent_->connection_state()->Update(ConnectionType::XMPP, name,
172 : ConnectionStatus::UP, ep,
173 : last_state_name);
174 : } else {
175 2 : agent_->connection_state()->Update(ConnectionType::XMPP, name,
176 : ConnectionStatus::DOWN, ep,
177 : last_state_name);
178 : }
179 2 : }
|