definition of the routing string to be deleted for wg and ikev2 connections
This commit is contained in:
parent
4abfc58ad0
commit
0d0c29493b
8 changed files with 111 additions and 60 deletions
|
|
@ -11,20 +11,18 @@ namespace adpinfo{
|
||||||
// {false,""} - no error
|
// {false,""} - no error
|
||||||
// {true,"descr"} - error with description
|
// {true,"descr"} - error with description
|
||||||
using RET_TYPE = std::tuple<bool, std::string>;
|
using RET_TYPE = std::tuple<bool, std::string>;
|
||||||
|
|
||||||
//using ROUTE_TABLE = std::tuple<std::string, std::string, std::string>;
|
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
/*
|
/*
|
||||||
* Getting the route table
|
* Getting the route table
|
||||||
*/
|
*/
|
||||||
typedef struct route_table{
|
typedef struct route_table{
|
||||||
std::string szDestIp;
|
std::string szDestIp{};
|
||||||
std::string szMaskIp;
|
std::string szMaskIp{};
|
||||||
std::string szGatewayIp;
|
std::string szGatewayIp{};
|
||||||
std::string szInterfaceIp;
|
std::string szInterfaceIp{};
|
||||||
|
unsigned long ulIfIndex{};
|
||||||
}route_table;
|
}route_table;
|
||||||
std::vector<std::tuple<std::string,std::string,std::string,std::string>>get_route_table();
|
std::vector</*std::tuple<std::string,std::string,std::string,std::string>*/route_table>get_route_table(std::string_view);
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
/*
|
/*
|
||||||
* The object uses for collect the information about active network adapters/interfaces
|
* The object uses for collect the information about active network adapters/interfaces
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ static std::string get_interface_ip(DWORD index){
|
||||||
break;
|
break;
|
||||||
adapter_iterator = adapter_iterator->Next;
|
adapter_iterator = adapter_iterator->Next;
|
||||||
}//end while
|
}//end while
|
||||||
if (adapter_iterator != nullptr || adapter_iterator != 0x0 || adapter_iterator != NULL)
|
if ( adapter_iterator != nullptr || adapter_iterator != 0x0 || adapter_iterator != NULL )
|
||||||
_ipaddr = std::string(adapter_iterator->IpAddressList.IpAddress.String, 16);
|
_ipaddr = std::string(adapter_iterator->IpAddressList.IpAddress.String, 16);
|
||||||
else
|
else
|
||||||
_ipaddr = "127.0.0.1";
|
_ipaddr = "127.0.0.1";
|
||||||
|
|
@ -120,9 +120,11 @@ static std::string get_interface_ip(DWORD index){
|
||||||
}
|
}
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
namespace adpinfo {
|
namespace adpinfo {
|
||||||
std::vector<std::tuple<std::string, std::string, std::string, std::string>>get_route_table(){
|
|
||||||
|
|
||||||
std::vector< std::tuple<std::string, std::string, std::string, std::string >>ret_table{};
|
std::vector<route_table>get_route_table(std::string_view ipifaddrs){
|
||||||
|
|
||||||
|
/*std::tuple<std::string, std::string, std::string, std::string >*/
|
||||||
|
std::vector<route_table>ret_table{};
|
||||||
PMIB_IPFORWARDTABLE p_table{nullptr};
|
PMIB_IPFORWARDTABLE p_table{nullptr};
|
||||||
struct in_addr ip_addr{};
|
struct in_addr ip_addr{};
|
||||||
DWORD size{};
|
DWORD size{};
|
||||||
|
|
@ -155,8 +157,45 @@ std::vector<std::tuple<std::string, std::string, std::string, std::string>>get_r
|
||||||
const auto &ifname = get_interface_ip(p_table->table[i].dwForwardIfIndex);
|
const auto &ifname = get_interface_ip(p_table->table[i].dwForwardIfIndex);
|
||||||
const auto &ifnameSize = ifname.length() + 1;
|
const auto &ifnameSize = ifname.length() + 1;
|
||||||
strcpy_s(szInterfaceIp, ifnameSize, ifname.data());
|
strcpy_s(szInterfaceIp, ifnameSize, ifname.data());
|
||||||
const auto &mt = std::make_tuple(std::string(szDestIp), std::string(szMaskIp), std::string(szGatewayIp), std::string(szInterfaceIp));
|
if (ipifaddrs.length() == 0){
|
||||||
ret_table.emplace_back(mt);
|
//std::make_tuple(std::string(szDestIp), std::string(szMaskIp), std::string(szGatewayIp), std::string(szInterfaceIp));
|
||||||
|
const route_table &mt = {
|
||||||
|
std::string(szDestIp),
|
||||||
|
std::string(szMaskIp),
|
||||||
|
std::string(szGatewayIp),
|
||||||
|
std::string(szInterfaceIp),
|
||||||
|
p_table->table[i].dwForwardIfIndex
|
||||||
|
};
|
||||||
|
ret_table.emplace_back(mt);
|
||||||
|
}else{
|
||||||
|
bool in_not_empty = (ifname.find(ipifaddrs) != std::string::npos);
|
||||||
|
//bool in_as_destIp = ( std::string(szDestIp).find(ipifaddrs) != std::string::npos);
|
||||||
|
bool destIp_as_zero = (std::string(szDestIp).find("0.0.0.0") != std::string::npos);
|
||||||
|
bool mask_as_zero = (std::string(szMaskIp).find("0.0.0.0") != std::string::npos);
|
||||||
|
// bool ip_the_same = (
|
||||||
|
// std::string(szDestIp).find(ipifaddrs) != std::string::npos &&
|
||||||
|
// std::string(szDestIp).find(szGatewayIp) != std::string::npos &&
|
||||||
|
// std::string(szDestIp).find(szInterfaceIp) != std::string::npos
|
||||||
|
// );
|
||||||
|
// bool not_default = (std::string(szDestIp).find("127.0.0.1") == std::string::npos);
|
||||||
|
|
||||||
|
if ( in_not_empty &&
|
||||||
|
// in_as_destIp &&
|
||||||
|
destIp_as_zero &&
|
||||||
|
mask_as_zero//) || ( ip_the_same && not_default )
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// finded
|
||||||
|
const route_table &mt = {
|
||||||
|
std::string(szDestIp),
|
||||||
|
std::string(szMaskIp),
|
||||||
|
std::string(szGatewayIp),
|
||||||
|
std::string(szInterfaceIp),
|
||||||
|
p_table->table[i].dwForwardIfIndex
|
||||||
|
};//std::make_tuple(std::string(szDestIp), std::string(szMaskIp), std::string(szGatewayIp), std::string(szInterfaceIp));
|
||||||
|
ret_table.emplace_back(mt);
|
||||||
|
}
|
||||||
|
}
|
||||||
}//end for
|
}//end for
|
||||||
}//end if
|
}//end if
|
||||||
return ret_table;
|
return ret_table;
|
||||||
|
|
@ -223,14 +262,14 @@ RET_TYPE NetAdpInfo::collect_adapters_data(){
|
||||||
_tmp->set_description(adapter_iterator->Description);
|
_tmp->set_description(adapter_iterator->Description);
|
||||||
_tmp->set_local_address(adapter_iterator->IpAddressList.IpAddress.String);
|
_tmp->set_local_address(adapter_iterator->IpAddressList.IpAddress.String);
|
||||||
std::string lgw = adapter_iterator->GatewayList.IpAddress.String;
|
std::string lgw = adapter_iterator->GatewayList.IpAddress.String;
|
||||||
if (lgw.length() == 0 || lgw.find("0.0.0.0") != std::string::npos)
|
// if (lgw.length() == 0 || lgw.find("0.0.0.0") != std::string::npos)
|
||||||
{
|
// {
|
||||||
//lgw = get_founded_route("8.8.8.8");
|
// //lgw = get_founded_route("8.8.8.8");
|
||||||
if (adapter_iterator->DhcpEnabled == 1)
|
// if (adapter_iterator->DhcpEnabled == 1)
|
||||||
{
|
// {
|
||||||
lgw = adapter_iterator->DhcpServer.IpAddress.String;
|
// lgw = adapter_iterator->DhcpServer.IpAddress.String;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
_tmp->set_local_gateway(lgw);
|
_tmp->set_local_gateway(lgw);
|
||||||
_tmp->set_route_gateway(get_route_gateway());
|
_tmp->set_route_gateway(get_route_gateway());
|
||||||
_adapters.emplace_back(_tmp);
|
_adapters.emplace_back(_tmp);
|
||||||
|
|
@ -251,10 +290,7 @@ RET_TYPE NetAdpInfo::get_adapter_info(std::string_view _adapter_name){
|
||||||
for (auto i = 0; i< len; ++i){
|
for (auto i = 0; i< len; ++i){
|
||||||
auto adap_name = _adapters[i]->get_name();
|
auto adap_name = _adapters[i]->get_name();
|
||||||
auto adap_desc = _adapters[i]->get_description();
|
auto adap_desc = _adapters[i]->get_description();
|
||||||
qDebug()<<"adap name : "<<QString::fromStdString(adap_name.data());
|
if ( adap_name.find(_adapter_name) != std::string::npos || adap_desc.find(_adapter_name) != std::string::npos ){
|
||||||
qDebug()<<"adap description : "<<QString::fromStdString(adap_desc.data());
|
|
||||||
qDebug()<<"find_string: "<<QString::fromStdString(_adapter_name.data());
|
|
||||||
if (adap_name.find(_adapter_name) != std::string::npos || adap_desc.find(_adapter_name) != std::string::npos){
|
|
||||||
_index_of_adapter = i;
|
_index_of_adapter = i;
|
||||||
return {false, ""};
|
return {false, ""};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
connData.clientId = Utils::getRandomString(16);
|
connData.clientId = Utils::getRandomString(16);
|
||||||
connData.password = Utils::getRandomString(16);
|
connData.password = Utils::getRandomString(16);
|
||||||
connData.password = "";
|
//connData.password = "";
|
||||||
|
|
||||||
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
|
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE
|
||||||
//SetEvent(gEvent_handle);
|
//SetEvent(gEvent_handle);
|
||||||
{
|
{
|
||||||
//get the network settings of adapters
|
//get the network settings of adapters
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(4));
|
std::this_thread::sleep_for(std::chrono::seconds(4));
|
||||||
std::string p1,p2,p3;
|
std::string p1,p2,p3;
|
||||||
const auto ret = adpInfo.get_adapter_info(tunnelName().toStdString());
|
const auto ret = adpInfo.get_adapter_info(tunnelName().toStdString());
|
||||||
if (std::get<0>(ret) == false){
|
if (std::get<0>(ret) == false){
|
||||||
|
|
@ -183,19 +183,23 @@ void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE
|
||||||
qDebug()<<"My ikev2 m_routeGateway "<<m_routeGateway;
|
qDebug()<<"My ikev2 m_routeGateway "<<m_routeGateway;
|
||||||
qDebug()<<"My ikev2 m_vpnLocalAddress "<<m_vpnLocalAddress;
|
qDebug()<<"My ikev2 m_vpnLocalAddress "<<m_vpnLocalAddress;
|
||||||
qDebug()<<"My ikev2 m_vpnGateway "<< m_vpnGateway;
|
qDebug()<<"My ikev2 m_vpnGateway "<< m_vpnGateway;
|
||||||
auto ret = adpinfo::get_route_table();
|
auto ret = adpinfo::get_route_table(p2.c_str());
|
||||||
qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
|
|
||||||
{
|
{
|
||||||
for (const auto &itret: ret){
|
for (const auto &itret: ret){
|
||||||
const auto ip = std::get<0>(itret);
|
const auto ip = itret.szDestIp;//std::get<0>(itret);
|
||||||
const auto msk = std::get<1>(itret);
|
const auto msk = itret.szMaskIp;//std::get<1>(itret);
|
||||||
const auto gw = std::get<2>(itret);
|
const auto gw = itret.szGatewayIp;//std::get<2>(itret);
|
||||||
const auto itf = std::get<3>(itret);
|
const auto itf = itret.szInterfaceIp;//std::get<3>(itret);
|
||||||
qDebug()<<"IP["<<ip.c_str()<<"]"<<"Mask["<<msk.c_str()<<"]"<<"gateway["<<gw.c_str()<<"]"<<"Interface["<<itf.c_str()<<"]";
|
const auto itfInd = itret.ulIfIndex;
|
||||||
|
qDebug()<<"IP["<<ip.c_str()<<"]"<<"Mask["<<msk.c_str()<<"]"<<"gateway["<<gw.c_str()<<"]"<<"Interface["<<itf.c_str()<<"]"<<"Interface index["<<itfInd<<"]";
|
||||||
|
emit route_avaible(QString::fromStdString(ip),
|
||||||
|
QString::fromStdString(msk),
|
||||||
|
QString::fromStdString(gw),
|
||||||
|
QString::fromStdString(itf),
|
||||||
|
itfInd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
//#ifdef Q_OS_WIN
|
||||||
DWORD CALLBACK rasCallback(UINT msg, RASCONNSTATE rascs, DWORD err);
|
//DWORD CALLBACK rasCallback(UINT msg, RASCONNSTATE rascs, DWORD err);
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
#endif // IPSEC_PROTOCOL_H
|
#endif // IPSEC_PROTOCOL_H
|
||||||
|
|
|
||||||
|
|
@ -263,19 +263,23 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
|
||||||
qDebug()<<"My openvpn m_routeGateway "<<m_routeGateway;
|
qDebug()<<"My openvpn m_routeGateway "<<m_routeGateway;
|
||||||
qDebug()<<"My openvpn m_vpnLocalAddress "<<m_vpnLocalAddress;
|
qDebug()<<"My openvpn m_vpnLocalAddress "<<m_vpnLocalAddress;
|
||||||
qDebug()<<"My openvpn m_vpnGateway "<< m_vpnGateway;
|
qDebug()<<"My openvpn m_vpnGateway "<< m_vpnGateway;
|
||||||
auto ret = adpinfo::get_route_table();
|
auto ret = adpinfo::get_route_table(p2.c_str());
|
||||||
qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
|
|
||||||
{
|
{
|
||||||
for (const auto &itret: ret){
|
for (const auto &itret: ret){
|
||||||
const auto ip = std::get<0>(itret);
|
const auto ip = itret.szDestIp;//std::get<0>(itret);
|
||||||
const auto msk = std::get<1>(itret);
|
const auto msk = itret.szMaskIp;//std::get<1>(itret);
|
||||||
const auto gw = std::get<2>(itret);
|
const auto gw = itret.szGatewayIp;//std::get<2>(itret);
|
||||||
const auto itf = std::get<3>(itret);
|
const auto itf = itret.szInterfaceIp;//std::get<3>(itret);
|
||||||
qDebug()<<"IP["<<ip.c_str()<<"]"<<"Mask["<<msk.c_str()<<"]"<<"gateway["<<gw.c_str()<<"]"<<"Interface["<<itf.c_str()<<"]";
|
const auto itfInd = itret.ulIfIndex;
|
||||||
|
qDebug()<<"IP["<<ip.c_str()<<"]"<<"Mask["<<msk.c_str()<<"]"<<"gateway["<<gw.c_str()<<"]"<<"Interface["<<itf.c_str()<<"]"<<"Interface index["<<itfInd<<"]";
|
||||||
|
emit route_avaible(QString::fromStdString(ip),
|
||||||
|
QString::fromStdString(msk),
|
||||||
|
QString::fromStdString(gw),
|
||||||
|
QString::fromStdString(itf),
|
||||||
|
itfInd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
qDebug()<<"We can't get information about active adapter:"<<QString::fromStdString(std::get<1>(ret));
|
qDebug()<<"We can't get information about active adapter:"<<QString::fromStdString(std::get<1>(ret));
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ signals:
|
||||||
void timeoutTimerEvent();
|
void timeoutTimerEvent();
|
||||||
void protocolError(amnezia::ErrorCode e);
|
void protocolError(amnezia::ErrorCode e);
|
||||||
|
|
||||||
|
void route_avaible(QString ip, QString mask, QString gateway, QString interface_ip, unsigned long interface_index);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void onTimeout(); // todo: remove?
|
virtual void onTimeout(); // todo: remove?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,31 +175,38 @@ ErrorCode WireguardProtocol::start()
|
||||||
{
|
{
|
||||||
//TODO:FIXME: without some ugly sleep we have't get a adapter parametrs
|
//TODO:FIXME: without some ugly sleep we have't get a adapter parametrs
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(4));
|
std::this_thread::sleep_for(std::chrono::seconds(4));
|
||||||
std::string p1{},p2{};//,p3;
|
std::string p1{},p2{},p3;
|
||||||
const auto &ret = adpInfo.get_adapter_info("WireGuard Tunnel");//serviceName().toStdString());//("AmneziaVPN IKEv2");
|
const auto &ret = adpInfo.get_adapter_info("WireGuard Tunnel");//serviceName().toStdString());//("AmneziaVPN IKEv2");
|
||||||
if (std::get<0>(ret) == false){
|
if (std::get<0>(ret) == false){
|
||||||
p1 = adpInfo.get_adapter_route_gateway();
|
p1 = adpInfo.get_adapter_route_gateway();
|
||||||
p2 = adpInfo.get_adapter_local_address();
|
p2 = adpInfo.get_adapter_local_address();
|
||||||
//p3 = adpInfo.get_adapter_local_gateway();
|
p3 = adpInfo.get_adapter_local_gateway();
|
||||||
m_routeGateway = QString::fromStdString(p1);
|
m_routeGateway = QString::fromStdString(p1);
|
||||||
m_vpnLocalAddress = QString::fromStdString(p2);
|
m_vpnLocalAddress = QString::fromStdString(p2);
|
||||||
m_vpnGateway = protocols::wireguard::defaultSubnetAddress;//QString::fromStdString(p3);
|
m_vpnGateway = QString::fromStdString(p3);
|
||||||
qDebug()<<"My wireguard m_routeGateway "<<m_routeGateway;
|
qDebug()<<"My wireguard m_routeGateway "<<m_routeGateway;
|
||||||
qDebug()<<"My wireguard m_vpnLocalAddress "<<m_vpnLocalAddress;
|
qDebug()<<"My wireguard m_vpnLocalAddress "<<m_vpnLocalAddress;
|
||||||
qDebug()<<"My wireguard m_vpnGateway "<< m_vpnGateway;
|
qDebug()<<"My wireguard m_vpnGateway "<< m_vpnGateway;
|
||||||
auto ret = adpinfo::get_route_table();
|
auto ret = adpinfo::get_route_table(p2.c_str());
|
||||||
qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
|
|
||||||
{
|
{
|
||||||
for (const auto &itret: ret){
|
for (const auto &itret: ret){
|
||||||
const auto ip = std::get<0>(itret);
|
const auto ip = itret.szDestIp;//std::get<0>(itret);
|
||||||
const auto msk = std::get<1>(itret);
|
const auto msk = itret.szMaskIp;//std::get<1>(itret);
|
||||||
const auto gw = std::get<2>(itret);
|
const auto gw = itret.szGatewayIp;//std::get<2>(itret);
|
||||||
const auto itf = std::get<3>(itret);
|
const auto itf = itret.szInterfaceIp;//std::get<3>(itret);
|
||||||
qDebug()<<"IP["<<ip.c_str()<<"]"<<"Mask["<<msk.c_str()<<"]"<<"gateway["<<gw.c_str()<<"]"<<"Interface["<<itf.c_str()<<"]";
|
const auto itfInd = itret.ulIfIndex;
|
||||||
}
|
qDebug()<<"IP["<<ip.c_str()<<"]"<<"Mask["<<msk.c_str()<<"]"<<"gateway["<<gw.c_str()<<"]"<<"Interface["<<itf.c_str()<<"]"<<"Interface index["<<itfInd<<"]";
|
||||||
|
emit route_avaible(QString::fromStdString(ip),
|
||||||
|
QString::fromStdString(msk),
|
||||||
|
QString::fromStdString(gw),
|
||||||
|
QString::fromStdString(itf),
|
||||||
|
itfInd);
|
||||||
|
//m_vpnGateway = QString::fromStdString(gw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
|
//qDebug()<<"My wireguard m_routeGateway "<<m_routeGateway;
|
||||||
|
//qDebug()<<"My wireguard m_vpnLocalAddress "<<m_vpnLocalAddress;
|
||||||
|
//qDebug()<<"My wireguard m_vpnGateway "<< m_vpnGateway;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
qDebug()<<"We can't get information about active adapter:"<<QString::fromStdString(std::get<1>(ret));
|
qDebug()<<"We can't get information about active adapter:"<<QString::fromStdString(std::get<1>(ret));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue